VB.NET LDAP Class

Discussion in 'ASP .Net Security' started by Jon Delano, Jul 29, 2004.

  1. Jon Delano

    Jon Delano Guest

    Hello

    After some effort I was able to come up with this class (converted from a C#
    example on MS' web site).
    Put this in a class in you vb.net web project and you should be able to
    authenticate a user against active directory and also retrieve their user
    groups.

    You must replace the LDAP://yourdomain with the actual domain that you'd
    like to authenticate against.

    Please do with this as you will (and use at your own risk):

    Imports System.DirectoryServices
    Imports System.Runtime.InteropServices
    Imports System.Globalization

    Public Class ADAuthenticate
    Private _path As String
    Private _filterAttribute As String
    Private _UserFirstName As String
    Private _UserLastName As String
    Private _UserPath As String
    Private _AuthenticationErrorString As String

    Public Function IsAuthenticated(ByVal UserName As String, ByVal Password
    As String) As Boolean
    ' authenticate the user and get some info about them
    Dim entry As New DirectoryEntry("LDAP://yourdomain", UserName,
    Password, System.DirectoryServices.AuthenticationTypes.Secure)
    Dim ds As New DirectorySearcher(entry)
    Dim myFilter As String = "(&(objectClass=user)(samaccountname=" +
    UserName + "))"

    ds.Filter = myFilter
    ds.PropertiesToLoad.Add("sn")
    ds.PropertiesToLoad.Add("GivenName")

    Try
    Dim sRslt As SearchResult = ds.FindOne
    If sRslt Is Nothing Then
    Return False
    Else
    _filterAttribute = UserName
    Dim propName As String
    Dim value As Object

    For Each propName In sRslt.Properties.PropertyNames
    For Each value In sRslt.Properties(propName)
    If propName = "sn" Then
    _UserLastName = value
    End If

    If propName = "givenname" Then
    _UserFirstName = value
    End If

    If propName = "adspath" Then
    _UserPath = value
    End If

    Next value
    Next propName

    Return True
    End If

    sRslt = Nothing
    ds = Nothing

    Catch ex As Exception
    _AuthenticationErrorString = ex.Message & "<br>" & ex.StackTrace
    Return False
    Finally
    entry.Dispose()
    entry = Nothing
    ds.Dispose()
    ds = Nothing
    End Try

    End Function

    Public ReadOnly Property LdapAuthenticationErrorString() As String
    Get
    Return _AuthenticationErrorString
    End Get
    End Property

    Public ReadOnly Property LdapUserFirstName() As String
    Get
    Return _UserFirstName
    End Get
    End Property

    Public ReadOnly Property LdapUserLastName() As String
    Get
    Return _UserLastName
    End Get
    End Property

    Public Function GetUserGroups(ByVal UserName As String, ByVal Password
    As String) As String
    ' get the groups the user belongs to
    Dim entry As New DirectoryEntry("LDAP://yourdomain", UserName,
    Password, System.DirectoryServices.AuthenticationTypes.Secure)
    Dim search = New DirectorySearcher(entry)

    search.Filter = "(&(objectClass=user)(cn=" + _filterAttribute + "))"
    search.PropertiesToLoad.Add("memberOf")

    Dim groupNames As New System.Text.StringBuilder()

    Try
    Dim result As SearchResult = search.FindOne()
    Dim propertyCount As Int32 = result.Properties("memberOf").Count
    Dim dn As String
    Dim equalsIndex As Int32, commaIndex As Int32
    Dim propertyCounter As Int32

    For propertyCounter = 0 To propertyCount - 1
    dn = result.Properties("memberOf")(propertyCounter)
    equalsIndex = dn.IndexOf("=", 1)
    commaIndex = dn.IndexOf(",", 1)
    If (-1 = equalsIndex) Then
    groupNames.Append(dn)
    Else
    groupNames.Append(dn.Substring((equalsIndex + 1),
    (commaIndex - equalsIndex) - 1))
    groupNames.Append("|")
    End If

    Next propertyCounter

    Catch ex As Exception
    Throw New Exception("Error obtaining group names. " +
    ex.Message)
    Finally
    entry.Dispose()
    entry = Nothing
    search = Nothing
    End Try

    Return groupNames.ToString()

    End Function
    End Class

    Good luck
    Jon
    Jon Delano, Jul 29, 2004
    #1
    1. Advertising

  2. Jon Delano

    Raterus Guest

    I hope you didn't spend too long converting this.. :)
    http://support.microsoft.com/default.aspx?scid=kb;EN-US;326340

    "Jon Delano" <> wrote in message news:94cOc.209721$Oq2.196851@attbi_s52...
    > Hello
    >
    > After some effort I was able to come up with this class (converted from a C#
    > example on MS' web site).
    > Put this in a class in you vb.net web project and you should be able to
    > authenticate a user against active directory and also retrieve their user
    > groups.
    >
    > You must replace the LDAP://yourdomain with the actual domain that you'd
    > like to authenticate against.
    >
    > Please do with this as you will (and use at your own risk):
    >
    > Imports System.DirectoryServices
    > Imports System.Runtime.InteropServices
    > Imports System.Globalization
    >
    > Public Class ADAuthenticate
    > Private _path As String
    > Private _filterAttribute As String
    > Private _UserFirstName As String
    > Private _UserLastName As String
    > Private _UserPath As String
    > Private _AuthenticationErrorString As String
    >
    > Public Function IsAuthenticated(ByVal UserName As String, ByVal Password
    > As String) As Boolean
    > ' authenticate the user and get some info about them
    > Dim entry As New DirectoryEntry("LDAP://yourdomain", UserName,
    > Password, System.DirectoryServices.AuthenticationTypes.Secure)
    > Dim ds As New DirectorySearcher(entry)
    > Dim myFilter As String = "(&(objectClass=user)(samaccountname=" +
    > UserName + "))"
    >
    > ds.Filter = myFilter
    > ds.PropertiesToLoad.Add("sn")
    > ds.PropertiesToLoad.Add("GivenName")
    >
    > Try
    > Dim sRslt As SearchResult = ds.FindOne
    > If sRslt Is Nothing Then
    > Return False
    > Else
    > _filterAttribute = UserName
    > Dim propName As String
    > Dim value As Object
    >
    > For Each propName In sRslt.Properties.PropertyNames
    > For Each value In sRslt.Properties(propName)
    > If propName = "sn" Then
    > _UserLastName = value
    > End If
    >
    > If propName = "givenname" Then
    > _UserFirstName = value
    > End If
    >
    > If propName = "adspath" Then
    > _UserPath = value
    > End If
    >
    > Next value
    > Next propName
    >
    > Return True
    > End If
    >
    > sRslt = Nothing
    > ds = Nothing
    >
    > Catch ex As Exception
    > _AuthenticationErrorString = ex.Message & "<br>" & ex.StackTrace
    > Return False
    > Finally
    > entry.Dispose()
    > entry = Nothing
    > ds.Dispose()
    > ds = Nothing
    > End Try
    >
    > End Function
    >
    > Public ReadOnly Property LdapAuthenticationErrorString() As String
    > Get
    > Return _AuthenticationErrorString
    > End Get
    > End Property
    >
    > Public ReadOnly Property LdapUserFirstName() As String
    > Get
    > Return _UserFirstName
    > End Get
    > End Property
    >
    > Public ReadOnly Property LdapUserLastName() As String
    > Get
    > Return _UserLastName
    > End Get
    > End Property
    >
    > Public Function GetUserGroups(ByVal UserName As String, ByVal Password
    > As String) As String
    > ' get the groups the user belongs to
    > Dim entry As New DirectoryEntry("LDAP://yourdomain", UserName,
    > Password, System.DirectoryServices.AuthenticationTypes.Secure)
    > Dim search = New DirectorySearcher(entry)
    >
    > search.Filter = "(&(objectClass=user)(cn=" + _filterAttribute + "))"
    > search.PropertiesToLoad.Add("memberOf")
    >
    > Dim groupNames As New System.Text.StringBuilder()
    >
    > Try
    > Dim result As SearchResult = search.FindOne()
    > Dim propertyCount As Int32 = result.Properties("memberOf").Count
    > Dim dn As String
    > Dim equalsIndex As Int32, commaIndex As Int32
    > Dim propertyCounter As Int32
    >
    > For propertyCounter = 0 To propertyCount - 1
    > dn = result.Properties("memberOf")(propertyCounter)
    > equalsIndex = dn.IndexOf("=", 1)
    > commaIndex = dn.IndexOf(",", 1)
    > If (-1 = equalsIndex) Then
    > groupNames.Append(dn)
    > Else
    > groupNames.Append(dn.Substring((equalsIndex + 1),
    > (commaIndex - equalsIndex) - 1))
    > groupNames.Append("|")
    > End If
    >
    > Next propertyCounter
    >
    > Catch ex As Exception
    > Throw New Exception("Error obtaining group names. " +
    > ex.Message)
    > Finally
    > entry.Dispose()
    > entry = Nothing
    > search = Nothing
    > End Try
    >
    > Return groupNames.ToString()
    >
    > End Function
    > End Class
    >
    > Good luck
    > Jon
    >
    >
    Raterus, Jul 29, 2004
    #2
    1. Advertising

  3. Actually, the code in that article is pretty bad and contains a number of
    flaws that in my opinion make it not worthy of emulation.

    Problems in the IsAuthenticated method include:
    - They don't use AuthenticationTypes.Secure, so password is sent in clear
    text over the network (!)
    - They don't properly call Dispose on the IDisposable objects, so memory
    leaks will occur, especially given the bugs in the Finalize method for
    DirectoryEntry in .NET 1.0 and 1.1
    - They make a bad assumption about the source the failure by catching the
    general Exception class instead of looking for the "bad credentials" HRESULT
    on the COMException that should be thrown
    - They make some bad assumptions that won't necessarily work in a
    multi-domain forest about looking up the user's user name

    Problems in the GetGroups methods include:
    - MemberOf attribute includes non-security groups, does not included nested
    group membership and does not include the primary group, so essentially that
    isn't the correct list
    - Use the CN attribute for making a security decision instead of
    sAMAccountName even though CN may not be unique in the directory (only the
    current container)
    - Parse the DN with a naive check ",", even though DN's can contain ","
    escaped with \
    - Fail to clean up and catch proper exception types as above

    As you can see, I'm pretty grumpy about that sample code. Perhaps someday
    I'll post something better or it can be fixed. In the mean time, please
    don't use it.

    Joe K.


    "Raterus" <> wrote in message
    news:...
    I hope you didn't spend too long converting this.. :)
    http://support.microsoft.com/default.aspx?scid=kb;EN-US;326340

    "Jon Delano" <> wrote in message
    news:94cOc.209721$Oq2.196851@attbi_s52...
    > Hello
    >
    > After some effort I was able to come up with this class (converted from a

    C#
    > example on MS' web site).
    > Put this in a class in you vb.net web project and you should be able to
    > authenticate a user against active directory and also retrieve their user
    > groups.
    >
    > You must replace the LDAP://yourdomain with the actual domain that you'd
    > like to authenticate against.
    >
    > Please do with this as you will (and use at your own risk):
    >
    > Imports System.DirectoryServices
    > Imports System.Runtime.InteropServices
    > Imports System.Globalization
    >
    > Public Class ADAuthenticate
    > Private _path As String
    > Private _filterAttribute As String
    > Private _UserFirstName As String
    > Private _UserLastName As String
    > Private _UserPath As String
    > Private _AuthenticationErrorString As String
    >
    > Public Function IsAuthenticated(ByVal UserName As String, ByVal

    Password
    > As String) As Boolean
    > ' authenticate the user and get some info about them
    > Dim entry As New DirectoryEntry("LDAP://yourdomain", UserName,
    > Password, System.DirectoryServices.AuthenticationTypes.Secure)
    > Dim ds As New DirectorySearcher(entry)
    > Dim myFilter As String = "(&(objectClass=user)(samaccountname=" +
    > UserName + "))"
    >
    > ds.Filter = myFilter
    > ds.PropertiesToLoad.Add("sn")
    > ds.PropertiesToLoad.Add("GivenName")
    >
    > Try
    > Dim sRslt As SearchResult = ds.FindOne
    > If sRslt Is Nothing Then
    > Return False
    > Else
    > _filterAttribute = UserName
    > Dim propName As String
    > Dim value As Object
    >
    > For Each propName In sRslt.Properties.PropertyNames
    > For Each value In sRslt.Properties(propName)
    > If propName = "sn" Then
    > _UserLastName = value
    > End If
    >
    > If propName = "givenname" Then
    > _UserFirstName = value
    > End If
    >
    > If propName = "adspath" Then
    > _UserPath = value
    > End If
    >
    > Next value
    > Next propName
    >
    > Return True
    > End If
    >
    > sRslt = Nothing
    > ds = Nothing
    >
    > Catch ex As Exception
    > _AuthenticationErrorString = ex.Message & "<br>" &

    ex.StackTrace
    > Return False
    > Finally
    > entry.Dispose()
    > entry = Nothing
    > ds.Dispose()
    > ds = Nothing
    > End Try
    >
    > End Function
    >
    > Public ReadOnly Property LdapAuthenticationErrorString() As String
    > Get
    > Return _AuthenticationErrorString
    > End Get
    > End Property
    >
    > Public ReadOnly Property LdapUserFirstName() As String
    > Get
    > Return _UserFirstName
    > End Get
    > End Property
    >
    > Public ReadOnly Property LdapUserLastName() As String
    > Get
    > Return _UserLastName
    > End Get
    > End Property
    >
    > Public Function GetUserGroups(ByVal UserName As String, ByVal Password
    > As String) As String
    > ' get the groups the user belongs to
    > Dim entry As New DirectoryEntry("LDAP://yourdomain", UserName,
    > Password, System.DirectoryServices.AuthenticationTypes.Secure)
    > Dim search = New DirectorySearcher(entry)
    >
    > search.Filter = "(&(objectClass=user)(cn=" + _filterAttribute +

    "))"
    > search.PropertiesToLoad.Add("memberOf")
    >
    > Dim groupNames As New System.Text.StringBuilder()
    >
    > Try
    > Dim result As SearchResult = search.FindOne()
    > Dim propertyCount As Int32 =

    result.Properties("memberOf").Count
    > Dim dn As String
    > Dim equalsIndex As Int32, commaIndex As Int32
    > Dim propertyCounter As Int32
    >
    > For propertyCounter = 0 To propertyCount - 1
    > dn = result.Properties("memberOf")(propertyCounter)
    > equalsIndex = dn.IndexOf("=", 1)
    > commaIndex = dn.IndexOf(",", 1)
    > If (-1 = equalsIndex) Then
    > groupNames.Append(dn)
    > Else
    > groupNames.Append(dn.Substring((equalsIndex + 1),
    > (commaIndex - equalsIndex) - 1))
    > groupNames.Append("|")
    > End If
    >
    > Next propertyCounter
    >
    > Catch ex As Exception
    > Throw New Exception("Error obtaining group names. " +
    > ex.Message)
    > Finally
    > entry.Dispose()
    > entry = Nothing
    > search = Nothing
    > End Try
    >
    > Return groupNames.ToString()
    >
    > End Function
    > End Class
    >
    > Good luck
    > Jon
    >
    >
    Joe Kaplan \(MVP - ADSI\), Jul 29, 2004
    #3
  4. Jon Delano

    Jon Delano Guest

    Well slap someone for just trying to help out some.

    I made no comments that it was the best option ... only that it worked for
    me in what I was doing (I did use AuthenticationTypes.Secure in my code by
    the way and I have added the dispose and set the objects to nothing.)

    At the very least it might give someone a starting point if they are lost.



    "Joe Kaplan (MVP - ADSI)" <> wrote
    in message news:%...
    > Actually, the code in that article is pretty bad and contains a number of
    > flaws that in my opinion make it not worthy of emulation.
    >
    > Problems in the IsAuthenticated method include:
    > - They don't use AuthenticationTypes.Secure, so password is sent in clear
    > text over the network (!)
    > - They don't properly call Dispose on the IDisposable objects, so memory
    > leaks will occur, especially given the bugs in the Finalize method for
    > DirectoryEntry in .NET 1.0 and 1.1
    > - They make a bad assumption about the source the failure by catching the
    > general Exception class instead of looking for the "bad credentials"

    HRESULT
    > on the COMException that should be thrown
    > - They make some bad assumptions that won't necessarily work in a
    > multi-domain forest about looking up the user's user name
    >
    > Problems in the GetGroups methods include:
    > - MemberOf attribute includes non-security groups, does not included

    nested
    > group membership and does not include the primary group, so essentially

    that
    > isn't the correct list
    > - Use the CN attribute for making a security decision instead of
    > sAMAccountName even though CN may not be unique in the directory (only the
    > current container)
    > - Parse the DN with a naive check ",", even though DN's can contain ","
    > escaped with \
    > - Fail to clean up and catch proper exception types as above
    >
    > As you can see, I'm pretty grumpy about that sample code. Perhaps someday
    > I'll post something better or it can be fixed. In the mean time, please
    > don't use it.
    >
    > Joe K.
    >
    >
    > "Raterus" <> wrote in message
    > news:...
    > I hope you didn't spend too long converting this.. :)
    > http://support.microsoft.com/default.aspx?scid=kb;EN-US;326340
    >
    > "Jon Delano" <> wrote in message
    > news:94cOc.209721$Oq2.196851@attbi_s52...
    > > Hello
    > >
    > > After some effort I was able to come up with this class (converted from

    a
    > C#
    > > example on MS' web site).
    > > Put this in a class in you vb.net web project and you should be able to
    > > authenticate a user against active directory and also retrieve their

    user
    > > groups.
    > >
    > > You must replace the LDAP://yourdomain with the actual domain that you'd
    > > like to authenticate against.
    > >
    > > Please do with this as you will (and use at your own risk):
    > >
    > > Imports System.DirectoryServices
    > > Imports System.Runtime.InteropServices
    > > Imports System.Globalization
    > >
    > > Public Class ADAuthenticate
    > > Private _path As String
    > > Private _filterAttribute As String
    > > Private _UserFirstName As String
    > > Private _UserLastName As String
    > > Private _UserPath As String
    > > Private _AuthenticationErrorString As String
    > >
    > > Public Function IsAuthenticated(ByVal UserName As String, ByVal

    > Password
    > > As String) As Boolean
    > > ' authenticate the user and get some info about them
    > > Dim entry As New DirectoryEntry("LDAP://yourdomain", UserName,
    > > Password, System.DirectoryServices.AuthenticationTypes.Secure)
    > > Dim ds As New DirectorySearcher(entry)
    > > Dim myFilter As String = "(&(objectClass=user)(samaccountname="

    +
    > > UserName + "))"
    > >
    > > ds.Filter = myFilter
    > > ds.PropertiesToLoad.Add("sn")
    > > ds.PropertiesToLoad.Add("GivenName")
    > >
    > > Try
    > > Dim sRslt As SearchResult = ds.FindOne
    > > If sRslt Is Nothing Then
    > > Return False
    > > Else
    > > _filterAttribute = UserName
    > > Dim propName As String
    > > Dim value As Object
    > >
    > > For Each propName In sRslt.Properties.PropertyNames
    > > For Each value In sRslt.Properties(propName)
    > > If propName = "sn" Then
    > > _UserLastName = value
    > > End If
    > >
    > > If propName = "givenname" Then
    > > _UserFirstName = value
    > > End If
    > >
    > > If propName = "adspath" Then
    > > _UserPath = value
    > > End If
    > >
    > > Next value
    > > Next propName
    > >
    > > Return True
    > > End If
    > >
    > > sRslt = Nothing
    > > ds = Nothing
    > >
    > > Catch ex As Exception
    > > _AuthenticationErrorString = ex.Message & "<br>" &

    > ex.StackTrace
    > > Return False
    > > Finally
    > > entry.Dispose()
    > > entry = Nothing
    > > ds.Dispose()
    > > ds = Nothing
    > > End Try
    > >
    > > End Function
    > >
    > > Public ReadOnly Property LdapAuthenticationErrorString() As String
    > > Get
    > > Return _AuthenticationErrorString
    > > End Get
    > > End Property
    > >
    > > Public ReadOnly Property LdapUserFirstName() As String
    > > Get
    > > Return _UserFirstName
    > > End Get
    > > End Property
    > >
    > > Public ReadOnly Property LdapUserLastName() As String
    > > Get
    > > Return _UserLastName
    > > End Get
    > > End Property
    > >
    > > Public Function GetUserGroups(ByVal UserName As String, ByVal

    Password
    > > As String) As String
    > > ' get the groups the user belongs to
    > > Dim entry As New DirectoryEntry("LDAP://yourdomain", UserName,
    > > Password, System.DirectoryServices.AuthenticationTypes.Secure)
    > > Dim search = New DirectorySearcher(entry)
    > >
    > > search.Filter = "(&(objectClass=user)(cn=" + _filterAttribute +

    > "))"
    > > search.PropertiesToLoad.Add("memberOf")
    > >
    > > Dim groupNames As New System.Text.StringBuilder()
    > >
    > > Try
    > > Dim result As SearchResult = search.FindOne()
    > > Dim propertyCount As Int32 =

    > result.Properties("memberOf").Count
    > > Dim dn As String
    > > Dim equalsIndex As Int32, commaIndex As Int32
    > > Dim propertyCounter As Int32
    > >
    > > For propertyCounter = 0 To propertyCount - 1
    > > dn = result.Properties("memberOf")(propertyCounter)
    > > equalsIndex = dn.IndexOf("=", 1)
    > > commaIndex = dn.IndexOf(",", 1)
    > > If (-1 = equalsIndex) Then
    > > groupNames.Append(dn)
    > > Else
    > > groupNames.Append(dn.Substring((equalsIndex + 1),
    > > (commaIndex - equalsIndex) - 1))
    > > groupNames.Append("|")
    > > End If
    > >
    > > Next propertyCounter
    > >
    > > Catch ex As Exception
    > > Throw New Exception("Error obtaining group names. " +
    > > ex.Message)
    > > Finally
    > > entry.Dispose()
    > > entry = Nothing
    > > search = Nothing
    > > End Try
    > >
    > > Return groupNames.ToString()
    > >
    > > End Function
    > > End Class
    > >
    > > Good luck
    > > Jon
    > >
    > >

    >
    >
    Jon Delano, Jul 30, 2004
    #4
  5. I wasn't criticizing your code Jon, I was criticizing the code in the
    article that Raterus pointed to when he suggested that you should have just
    used it as an example instead. That is a KBase article and needs to be held
    to a higher standard. It is a big pet peeve of mine.

    Your code is basically fine by me! Sorry for the confusion :)

    Joe K.

    "Jon Delano" <> wrote in message
    news:sgsOc.216053$XM6.171248@attbi_s53...
    > Well slap someone for just trying to help out some.
    >
    > I made no comments that it was the best option ... only that it worked for
    > me in what I was doing (I did use AuthenticationTypes.Secure in my code by
    > the way and I have added the dispose and set the objects to nothing.)
    >
    > At the very least it might give someone a starting point if they are lost.
    >
    >
    >
    > "Joe Kaplan (MVP - ADSI)" <> wrote
    > in message news:%...
    > > Actually, the code in that article is pretty bad and contains a number

    of
    > > flaws that in my opinion make it not worthy of emulation.
    > >
    > > Problems in the IsAuthenticated method include:
    > > - They don't use AuthenticationTypes.Secure, so password is sent in

    clear
    > > text over the network (!)
    > > - They don't properly call Dispose on the IDisposable objects, so

    memory
    > > leaks will occur, especially given the bugs in the Finalize method for
    > > DirectoryEntry in .NET 1.0 and 1.1
    > > - They make a bad assumption about the source the failure by catching

    the
    > > general Exception class instead of looking for the "bad credentials"

    > HRESULT
    > > on the COMException that should be thrown
    > > - They make some bad assumptions that won't necessarily work in a
    > > multi-domain forest about looking up the user's user name
    > >
    > > Problems in the GetGroups methods include:
    > > - MemberOf attribute includes non-security groups, does not included

    > nested
    > > group membership and does not include the primary group, so essentially

    > that
    > > isn't the correct list
    > > - Use the CN attribute for making a security decision instead of
    > > sAMAccountName even though CN may not be unique in the directory (only

    the
    > > current container)
    > > - Parse the DN with a naive check ",", even though DN's can contain ","
    > > escaped with \
    > > - Fail to clean up and catch proper exception types as above
    > >
    > > As you can see, I'm pretty grumpy about that sample code. Perhaps

    someday
    > > I'll post something better or it can be fixed. In the mean time, please
    > > don't use it.
    > >
    > > Joe K.
    > >
    > >
    > > "Raterus" <> wrote in message
    > > news:...
    > > I hope you didn't spend too long converting this.. :)
    > > http://support.microsoft.com/default.aspx?scid=kb;EN-US;326340
    > >
    > > "Jon Delano" <> wrote in message
    > > news:94cOc.209721$Oq2.196851@attbi_s52...
    > > > Hello
    > > >
    > > > After some effort I was able to come up with this class (converted

    from
    > a
    > > C#
    > > > example on MS' web site).
    > > > Put this in a class in you vb.net web project and you should be able

    to
    > > > authenticate a user against active directory and also retrieve their

    > user
    > > > groups.
    > > >
    > > > You must replace the LDAP://yourdomain with the actual domain that

    you'd
    > > > like to authenticate against.
    > > >
    > > > Please do with this as you will (and use at your own risk):
    > > >
    > > > Imports System.DirectoryServices
    > > > Imports System.Runtime.InteropServices
    > > > Imports System.Globalization
    > > >
    > > > Public Class ADAuthenticate
    > > > Private _path As String
    > > > Private _filterAttribute As String
    > > > Private _UserFirstName As String
    > > > Private _UserLastName As String
    > > > Private _UserPath As String
    > > > Private _AuthenticationErrorString As String
    > > >
    > > > Public Function IsAuthenticated(ByVal UserName As String, ByVal

    > > Password
    > > > As String) As Boolean
    > > > ' authenticate the user and get some info about them
    > > > Dim entry As New DirectoryEntry("LDAP://yourdomain", UserName,
    > > > Password, System.DirectoryServices.AuthenticationTypes.Secure)
    > > > Dim ds As New DirectorySearcher(entry)
    > > > Dim myFilter As String =

    "(&(objectClass=user)(samaccountname="
    > +
    > > > UserName + "))"
    > > >
    > > > ds.Filter = myFilter
    > > > ds.PropertiesToLoad.Add("sn")
    > > > ds.PropertiesToLoad.Add("GivenName")
    > > >
    > > > Try
    > > > Dim sRslt As SearchResult = ds.FindOne
    > > > If sRslt Is Nothing Then
    > > > Return False
    > > > Else
    > > > _filterAttribute = UserName
    > > > Dim propName As String
    > > > Dim value As Object
    > > >
    > > > For Each propName In sRslt.Properties.PropertyNames
    > > > For Each value In sRslt.Properties(propName)
    > > > If propName = "sn" Then
    > > > _UserLastName = value
    > > > End If
    > > >
    > > > If propName = "givenname" Then
    > > > _UserFirstName = value
    > > > End If
    > > >
    > > > If propName = "adspath" Then
    > > > _UserPath = value
    > > > End If
    > > >
    > > > Next value
    > > > Next propName
    > > >
    > > > Return True
    > > > End If
    > > >
    > > > sRslt = Nothing
    > > > ds = Nothing
    > > >
    > > > Catch ex As Exception
    > > > _AuthenticationErrorString = ex.Message & "<br>" &

    > > ex.StackTrace
    > > > Return False
    > > > Finally
    > > > entry.Dispose()
    > > > entry = Nothing
    > > > ds.Dispose()
    > > > ds = Nothing
    > > > End Try
    > > >
    > > > End Function
    > > >
    > > > Public ReadOnly Property LdapAuthenticationErrorString() As String
    > > > Get
    > > > Return _AuthenticationErrorString
    > > > End Get
    > > > End Property
    > > >
    > > > Public ReadOnly Property LdapUserFirstName() As String
    > > > Get
    > > > Return _UserFirstName
    > > > End Get
    > > > End Property
    > > >
    > > > Public ReadOnly Property LdapUserLastName() As String
    > > > Get
    > > > Return _UserLastName
    > > > End Get
    > > > End Property
    > > >
    > > > Public Function GetUserGroups(ByVal UserName As String, ByVal

    > Password
    > > > As String) As String
    > > > ' get the groups the user belongs to
    > > > Dim entry As New DirectoryEntry("LDAP://yourdomain", UserName,
    > > > Password, System.DirectoryServices.AuthenticationTypes.Secure)
    > > > Dim search = New DirectorySearcher(entry)
    > > >
    > > > search.Filter = "(&(objectClass=user)(cn=" + _filterAttribute

    +
    > > "))"
    > > > search.PropertiesToLoad.Add("memberOf")
    > > >
    > > > Dim groupNames As New System.Text.StringBuilder()
    > > >
    > > > Try
    > > > Dim result As SearchResult = search.FindOne()
    > > > Dim propertyCount As Int32 =

    > > result.Properties("memberOf").Count
    > > > Dim dn As String
    > > > Dim equalsIndex As Int32, commaIndex As Int32
    > > > Dim propertyCounter As Int32
    > > >
    > > > For propertyCounter = 0 To propertyCount - 1
    > > > dn = result.Properties("memberOf")(propertyCounter)
    > > > equalsIndex = dn.IndexOf("=", 1)
    > > > commaIndex = dn.IndexOf(",", 1)
    > > > If (-1 = equalsIndex) Then
    > > > groupNames.Append(dn)
    > > > Else
    > > > groupNames.Append(dn.Substring((equalsIndex + 1),
    > > > (commaIndex - equalsIndex) - 1))
    > > > groupNames.Append("|")
    > > > End If
    > > >
    > > > Next propertyCounter
    > > >
    > > > Catch ex As Exception
    > > > Throw New Exception("Error obtaining group names. " +
    > > > ex.Message)
    > > > Finally
    > > > entry.Dispose()
    > > > entry = Nothing
    > > > search = Nothing
    > > > End Try
    > > >
    > > > Return groupNames.ToString()
    > > >
    > > > End Function
    > > > End Class
    > > >
    > > > Good luck
    > > > Jon
    > > >
    > > >

    > >
    > >

    >
    >
    Joe Kaplan \(MVP - ADSI\), Jul 30, 2004
    #5
  6. Jon Delano

    Raterus Guest

    This isn't the first time Joe has mentioned the faults in this code either, when I was trying to do what you are doing, I kept finding posts by him suggesting better ways, so I listened. Here is how I've been getting my groups after looking through all of his suggestions. It basically revolves around the use of tokenGroups.

    I modified this too, for my purposes I needed a delimited string of groupnames. You also have to create the DirectoryEntry based on the user you are interested in, in the class I created I had already done that, so that is why you don't see "dn" declared, just used.

    Private Function GetGroups() As String
    Dim octetSid As String
    Dim binarySid() As Byte
    Dim binarySids As PropertyValueCollection
    Dim iterator As Integer
    Dim groups As StringBuilder = New StringBuilder

    Dim gEntry As DirectoryEntry = New DirectoryEntry("LDAP://" & dn)
    gEntry.RefreshCache(New String() {"tokenGroups"})

    binarySids = gEntry.Properties("tokenGroups")
    For iterator = 0 To binarySids.Count - 1
    binarySid = CType(binarySids(iterator), Byte())
    octetSid = ConvertToOctetString(binarySid)

    Dim groupPath As String = String.Format("<SID={0}>", octetSid)
    Dim groupEntry As New DirectoryEntry("LDAP://" & groupPath)

    If iterator > 0 Then
    groups.Append("|")
    End If
    groups.Append(groupEntry.Properties("sAMAccountName").Value.ToString())

    Next

    Return groups.ToString

    End Function

    Private Function ConvertToOctetString(ByVal value As Byte()) As String
    Dim iterator As Integer
    Dim builder As System.Text.StringBuilder

    builder = New System.Text.StringBuilder((value.GetUpperBound(0) + 1) * 2)
    For iterator = 0 To value.GetUpperBound(0)
    builder.Append(value(iterator).ToString("x2"))
    Next

    Return builder.ToString()
    End Function

    --Michael

    "Joe Kaplan (MVP - ADSI)" <> wrote in message news:...
    > I wasn't criticizing your code Jon, I was criticizing the code in the
    > article that Raterus pointed to when he suggested that you should have just
    > used it as an example instead. That is a KBase article and needs to be held
    > to a higher standard. It is a big pet peeve of mine.
    >
    > Your code is basically fine by me! Sorry for the confusion :)
    >
    > Joe K.
    Raterus, Jul 30, 2004
    #6
  7. Sorry for going off on your post too. Every time I see that article
    mentioned, it makes me cringe though, so I tend to over react.

    The code below is a much more solid approach. I'd recommend calling Dispose
    on your DirectoryEntry objects in a finally block to ensure that you don't
    leak memory, but this technique works.

    I have a newer technique that uses the DirectorySearcher to do a search for
    all of the SIDs at once which is a fair amount faster than binding to each
    individual group, but that is just an optimization. It probably only
    matters if the user is in many groups.

    The downside of all of these approaches is that you should really use the
    fully qualified group name (domain\name), but it isn't easy to figure out
    the NETBIOS name of the domain given the SID (possible, just not easy). I'm
    thinking of trying to use the DsCrackNames API via p/invoke to accomplish
    this in my next attempt.

    Joe K.

    "Raterus" <> wrote in message
    news:...
    This isn't the first time Joe has mentioned the faults in this code either,
    when I was trying to do what you are doing, I kept finding posts by him
    suggesting better ways, so I listened. Here is how I've been getting my
    groups after looking through all of his suggestions. It basically revolves
    around the use of tokenGroups.

    I modified this too, for my purposes I needed a delimited string of
    groupnames. You also have to create the DirectoryEntry based on the user
    you are interested in, in the class I created I had already done that, so
    that is why you don't see "dn" declared, just used.

    Private Function GetGroups() As String
    Dim octetSid As String
    Dim binarySid() As Byte
    Dim binarySids As PropertyValueCollection
    Dim iterator As Integer
    Dim groups As StringBuilder = New StringBuilder

    Dim gEntry As DirectoryEntry = New DirectoryEntry("LDAP://" & dn)
    gEntry.RefreshCache(New String() {"tokenGroups"})

    binarySids = gEntry.Properties("tokenGroups")
    For iterator = 0 To binarySids.Count - 1
    binarySid = CType(binarySids(iterator), Byte())
    octetSid = ConvertToOctetString(binarySid)

    Dim groupPath As String = String.Format("<SID={0}>", octetSid)
    Dim groupEntry As New DirectoryEntry("LDAP://" & groupPath)

    If iterator > 0 Then
    groups.Append("|")
    End If

    groups.Append(groupEntry.Properties("sAMAccountName").Value.ToString())

    Next

    Return groups.ToString

    End Function

    Private Function ConvertToOctetString(ByVal value As Byte()) As String
    Dim iterator As Integer
    Dim builder As System.Text.StringBuilder

    builder = New System.Text.StringBuilder((value.GetUpperBound(0) + 1)
    * 2)
    For iterator = 0 To value.GetUpperBound(0)
    builder.Append(value(iterator).ToString("x2"))
    Next

    Return builder.ToString()
    End Function

    --Michael

    "Joe Kaplan (MVP - ADSI)" <> wrote
    in message news:...
    > I wasn't criticizing your code Jon, I was criticizing the code in the
    > article that Raterus pointed to when he suggested that you should have

    just
    > used it as an example instead. That is a KBase article and needs to be

    held
    > to a higher standard. It is a big pet peeve of mine.
    >
    > Your code is basically fine by me! Sorry for the confusion :)
    >
    > Joe K.
    Joe Kaplan \(MVP - ADSI\), Jul 30, 2004
    #7
  8. Jon Delano

    Jon Delano Guest

    No problem ...I just noticed that we have emailed before (I had used ADO to
    first query Active Directory) ... and I sent you what I came up with. (Its
    the IsAuthenticated part of the class I created later) ... after you pointed
    me in the direction of the DirectoryEntry stuff.

    LOL .. too funny.

    Anyway, thanks and I'm sorry I took it the wrong way ... long weeks and days
    ....

    Take care
    Jon


    "Joe Kaplan (MVP - ADSI)" <> wrote
    in message news:...
    > I wasn't criticizing your code Jon, I was criticizing the code in the
    > article that Raterus pointed to when he suggested that you should have

    just
    > used it as an example instead. That is a KBase article and needs to be

    held
    > to a higher standard. It is a big pet peeve of mine.
    >
    > Your code is basically fine by me! Sorry for the confusion :)
    >
    > Joe K.
    >
    > "Jon Delano" <> wrote in message
    > news:sgsOc.216053$XM6.171248@attbi_s53...
    > > Well slap someone for just trying to help out some.
    > >
    > > I made no comments that it was the best option ... only that it worked

    for
    > > me in what I was doing (I did use AuthenticationTypes.Secure in my code

    by
    > > the way and I have added the dispose and set the objects to nothing.)
    > >
    > > At the very least it might give someone a starting point if they are

    lost.
    > >
    > >
    > >
    > > "Joe Kaplan (MVP - ADSI)" <>

    wrote
    > > in message news:%...
    > > > Actually, the code in that article is pretty bad and contains a number

    > of
    > > > flaws that in my opinion make it not worthy of emulation.
    > > >
    > > > Problems in the IsAuthenticated method include:
    > > > - They don't use AuthenticationTypes.Secure, so password is sent in

    > clear
    > > > text over the network (!)
    > > > - They don't properly call Dispose on the IDisposable objects, so

    > memory
    > > > leaks will occur, especially given the bugs in the Finalize method for
    > > > DirectoryEntry in .NET 1.0 and 1.1
    > > > - They make a bad assumption about the source the failure by catching

    > the
    > > > general Exception class instead of looking for the "bad credentials"

    > > HRESULT
    > > > on the COMException that should be thrown
    > > > - They make some bad assumptions that won't necessarily work in a
    > > > multi-domain forest about looking up the user's user name
    > > >
    > > > Problems in the GetGroups methods include:
    > > > - MemberOf attribute includes non-security groups, does not included

    > > nested
    > > > group membership and does not include the primary group, so

    essentially
    > > that
    > > > isn't the correct list
    > > > - Use the CN attribute for making a security decision instead of
    > > > sAMAccountName even though CN may not be unique in the directory (only

    > the
    > > > current container)
    > > > - Parse the DN with a naive check ",", even though DN's can contain

    ","
    > > > escaped with \
    > > > - Fail to clean up and catch proper exception types as above
    > > >
    > > > As you can see, I'm pretty grumpy about that sample code. Perhaps

    > someday
    > > > I'll post something better or it can be fixed. In the mean time,

    please
    > > > don't use it.
    > > >
    > > > Joe K.
    > > >
    > > >
    > > > "Raterus" <> wrote in message
    > > > news:...
    > > > I hope you didn't spend too long converting this.. :)
    > > > http://support.microsoft.com/default.aspx?scid=kb;EN-US;326340
    > > >
    > > > "Jon Delano" <> wrote in message
    > > > news:94cOc.209721$Oq2.196851@attbi_s52...
    > > > > Hello
    > > > >
    > > > > After some effort I was able to come up with this class (converted

    > from
    > > a
    > > > C#
    > > > > example on MS' web site).
    > > > > Put this in a class in you vb.net web project and you should be able

    > to
    > > > > authenticate a user against active directory and also retrieve their

    > > user
    > > > > groups.
    > > > >
    > > > > You must replace the LDAP://yourdomain with the actual domain that

    > you'd
    > > > > like to authenticate against.
    > > > >
    > > > > Please do with this as you will (and use at your own risk):
    > > > >
    > > > > Imports System.DirectoryServices
    > > > > Imports System.Runtime.InteropServices
    > > > > Imports System.Globalization
    > > > >
    > > > > Public Class ADAuthenticate
    > > > > Private _path As String
    > > > > Private _filterAttribute As String
    > > > > Private _UserFirstName As String
    > > > > Private _UserLastName As String
    > > > > Private _UserPath As String
    > > > > Private _AuthenticationErrorString As String
    > > > >
    > > > > Public Function IsAuthenticated(ByVal UserName As String, ByVal
    > > > Password
    > > > > As String) As Boolean
    > > > > ' authenticate the user and get some info about them
    > > > > Dim entry As New DirectoryEntry("LDAP://yourdomain",

    UserName,
    > > > > Password, System.DirectoryServices.AuthenticationTypes.Secure)
    > > > > Dim ds As New DirectorySearcher(entry)
    > > > > Dim myFilter As String =

    > "(&(objectClass=user)(samaccountname="
    > > +
    > > > > UserName + "))"
    > > > >
    > > > > ds.Filter = myFilter
    > > > > ds.PropertiesToLoad.Add("sn")
    > > > > ds.PropertiesToLoad.Add("GivenName")
    > > > >
    > > > > Try
    > > > > Dim sRslt As SearchResult = ds.FindOne
    > > > > If sRslt Is Nothing Then
    > > > > Return False
    > > > > Else
    > > > > _filterAttribute = UserName
    > > > > Dim propName As String
    > > > > Dim value As Object
    > > > >
    > > > > For Each propName In sRslt.Properties.PropertyNames
    > > > > For Each value In sRslt.Properties(propName)
    > > > > If propName = "sn" Then
    > > > > _UserLastName = value
    > > > > End If
    > > > >
    > > > > If propName = "givenname" Then
    > > > > _UserFirstName = value
    > > > > End If
    > > > >
    > > > > If propName = "adspath" Then
    > > > > _UserPath = value
    > > > > End If
    > > > >
    > > > > Next value
    > > > > Next propName
    > > > >
    > > > > Return True
    > > > > End If
    > > > >
    > > > > sRslt = Nothing
    > > > > ds = Nothing
    > > > >
    > > > > Catch ex As Exception
    > > > > _AuthenticationErrorString = ex.Message & "<br>" &
    > > > ex.StackTrace
    > > > > Return False
    > > > > Finally
    > > > > entry.Dispose()
    > > > > entry = Nothing
    > > > > ds.Dispose()
    > > > > ds = Nothing
    > > > > End Try
    > > > >
    > > > > End Function
    > > > >
    > > > > Public ReadOnly Property LdapAuthenticationErrorString() As

    String
    > > > > Get
    > > > > Return _AuthenticationErrorString
    > > > > End Get
    > > > > End Property
    > > > >
    > > > > Public ReadOnly Property LdapUserFirstName() As String
    > > > > Get
    > > > > Return _UserFirstName
    > > > > End Get
    > > > > End Property
    > > > >
    > > > > Public ReadOnly Property LdapUserLastName() As String
    > > > > Get
    > > > > Return _UserLastName
    > > > > End Get
    > > > > End Property
    > > > >
    > > > > Public Function GetUserGroups(ByVal UserName As String, ByVal

    > > Password
    > > > > As String) As String
    > > > > ' get the groups the user belongs to
    > > > > Dim entry As New DirectoryEntry("LDAP://yourdomain",

    UserName,
    > > > > Password, System.DirectoryServices.AuthenticationTypes.Secure)
    > > > > Dim search = New DirectorySearcher(entry)
    > > > >
    > > > > search.Filter = "(&(objectClass=user)(cn=" +

    _filterAttribute
    > +
    > > > "))"
    > > > > search.PropertiesToLoad.Add("memberOf")
    > > > >
    > > > > Dim groupNames As New System.Text.StringBuilder()
    > > > >
    > > > > Try
    > > > > Dim result As SearchResult = search.FindOne()
    > > > > Dim propertyCount As Int32 =
    > > > result.Properties("memberOf").Count
    > > > > Dim dn As String
    > > > > Dim equalsIndex As Int32, commaIndex As Int32
    > > > > Dim propertyCounter As Int32
    > > > >
    > > > > For propertyCounter = 0 To propertyCount - 1
    > > > > dn = result.Properties("memberOf")(propertyCounter)
    > > > > equalsIndex = dn.IndexOf("=", 1)
    > > > > commaIndex = dn.IndexOf(",", 1)
    > > > > If (-1 = equalsIndex) Then
    > > > > groupNames.Append(dn)
    > > > > Else
    > > > > groupNames.Append(dn.Substring((equalsIndex +

    1),
    > > > > (commaIndex - equalsIndex) - 1))
    > > > > groupNames.Append("|")
    > > > > End If
    > > > >
    > > > > Next propertyCounter
    > > > >
    > > > > Catch ex As Exception
    > > > > Throw New Exception("Error obtaining group names. " +
    > > > > ex.Message)
    > > > > Finally
    > > > > entry.Dispose()
    > > > > entry = Nothing
    > > > > search = Nothing
    > > > > End Try
    > > > >
    > > > > Return groupNames.ToString()
    > > > >
    > > > > End Function
    > > > > End Class
    > > > >
    > > > > Good luck
    > > > > Jon
    > > > >
    > > > >
    > > >
    > > >

    > >
    > >

    >
    >
    Jon Delano, Aug 2, 2004
    #8
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    1
    Views:
    515
    Raymond DeCampo
    Feb 21, 2006
  2. rcmn
    Replies:
    1
    Views:
    328
    =?ISO-8859-1?Q?Michael_Str=F6der?=
    Nov 6, 2006
  3. Jason Wold

    using LDAP Controls in ruby-ldap

    Jason Wold, Nov 4, 2004, in forum: Ruby
    Replies:
    5
    Views:
    231
  4. Replies:
    1
    Views:
    194
    Austin Ziegler
    Oct 11, 2006
  5. dacat

    Net::LDAP vs ruby/ldap

    dacat, Apr 27, 2007, in forum: Ruby
    Replies:
    3
    Views:
    283
    Ian Macdonald
    May 18, 2007
Loading...

Share This Page