Active Directory LDAP查询 - 希望过滤掉已禁用的用户,但缺少属性

时间:2015-07-02 09:19:41

标签: c# asp.net active-directory ldap

我正在编写一些代码来使用LDAP连接查询Active Directory。我只对用户感兴趣,而且我正在测试AD的虚拟实例。

它运作良好 - 我指定要返回的特定属性并使用这些属性返回结果。

最后一项挑战是过滤掉已停用的用户。在网络上我发现这需要DirectorySearcher Filter属性中的以下条款:

(!(userAccountControl:1.2.840.113556.1.4.803:=2))

然而,这并没有奏效。某些残疾用户总是被退回。为了调查我写了一个小控制台应用程序来揭示所有用户属性:

enter image description here

请注意,我们会返回2个用户,但只有一个用户拥有'帐户控件'属性? (请注意,标签'帐户控件'正在报告userAccountControl属性。)第二个用户,无论是启用还是禁用,都不会返回userAccountControl属性,因此我无法对其进行过滤基于此。

有人可以解释一下吗?

*更新*

添加一些执行查询的代码:

            using (DirectoryEntry de = new DirectoryEntry(ConnectionString))
            {
                //de.Path = Path;
                de.Username = Username;
                de.Password = Password;

                DirectorySearcher directorySearcher = new DirectorySearcher(de);
                directorySearcher.PageSize = 1001;// To Pull up more than 100 records.

                // Note that the userAccountControl clause excludes disabled users
                directorySearcher.Filter = string.Format("(&(objectClass=user){0}{1})", DisabledUserFilter(), query);

                Console.WriteLine("------------");
                Console.WriteLine(directorySearcher.Filter);

                Attributes.ForEach(a => directorySearcher.PropertiesToLoad.Add(a.Key));

                directorySearcher.SearchScope = SearchScope.Subtree;

                try
                {
                    var result = directorySearcher.FindAll();
                    ...

2 个答案:

答案 0 :(得分:2)

听起来您发现其他对象类型没有" userAccountControl" -attribute(例如Contacts)。试试这个saerch过滤器:

(&(userAccountControl=*)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))

这个测试是否完全设置了属性,并且还设置了未设置的禁用位。

提示:您还应该包含" objectCategory = user"的过滤器。在搜索用户帐户时,因为计算机帐户也有对象类" user"和" userAccountControl" -attribute。

答案 1 :(得分:0)

您使用哪些课程来获取AD条目的详细信息?

以下是我们用来检测AD条目状态的一些代码(对不起VB.NET):

Private Sub verifyUser(ByVal username As String, ByVal password As String)
    '1. Check if username exists in AD
    Dim userPath As String = String.Empty
    If Not findUser(username, userPath) Then
        'not found
        Exit Sub
    End If

    '2. Get AD Entry
    Using adsUser As New DirectoryEntry(userPath, ADsUserName, ADsPassword)

    '3. Is account Locked
        If isLockedHack(adsUser) Then
            'account is locked
            Exit Sub
        End If

        '4. Is account Disabled 
        If isDisabled(adsUser) Then
            'account is disabled
            Exit Sub
        End If

        '5. Has account expired
        If hasAccountExpired(adsUser) Then
            'account expired
            Exit Sub
        End If

        '6. Check whether user has to change password at next login
        If hasToChangePassAtNextLoginHack(adsUser) Then
            'change pass next login                
            Exit Sub
        End If

        '7. Has password expired
        If hasPasswordExpired(adsUser) Then
            'password expired                
            Exit Sub
        End If

        '8. Check whether password entered is okay
        If isPassCorrect(userPath, username, password) Then
            'account is fine
        Else
            'account is fine but password is incorrect
        End If
    End Using
End Sub

Private strMaxPwdAge As String = "maxPwdAge"
Private strPwdLastSet As String = "pwdLastSet"
Private strLockoutTime As String = "lockoutTime"
Private strAccountDisabled As String = "AccountDisabled"

Private Function hasToChangePassAtNextLoginHack(ByVal adsUser As DirectoryEntry) As Boolean
    If adsUser.Properties.Contains(strPwdLastSet) Then
        Dim objPwdLastSet As Object = adsUser.Properties(strPwdLastSet)(0)
        If objPwdLastSet.LowPart = 0 Then
            Return True
        End If
    End If
    Return False

End Function

Private Function hasPasswordExpired(ByVal adsUser As DirectoryEntry) As Boolean
    Dim pwdLastSet As Object
    pwdLastSet = adsUser.Properties(strPwdLastSet).Value

    Dim pwdDate As DateTime = GetDateFromLargeInteger(pwdLastSet)
    Dim today As DateTime = Date.Now

    If pwdDate.AddDays(CInt(getMaxPwdAge())).CompareTo(today) < 0 Then
        Return True
    Else
        Return False
    End If
    Return False
End Function

Private Function isLockedHack(ByVal adsUser As DirectoryEntry) As Boolean
    Dim pcoll As PropertyCollection = adsUser.Properties

    If adsUser.Properties.Contains(strLockoutTime) Then
        Dim oli2 As Object = adsUser.Properties(strLockoutTime)(0)

        Dim timeVal As Long = (oli2.HighPart * &H100000000) + oli2.LowPart
        If timeVal > 0 Then
            Return True
        End If
    End If
    Return False
End Function

Private Function isPassCorrect(ByVal userPath As String, ByVal user As String, _
    ByVal pass As String) As Boolean

    Try
        Using adsUser As New DirectoryEntry(userPath, user, pass)
            Dim natObj As Object = adsUser.NativeObject
            adsUser.Dispose()
        End Using
    Catch ex As Exception
        Return False
    End Try
    Return True
End Function

Private Function isLocked(ByVal adsUser As DirectoryEntry) As Boolean

    Dim binIL As Boolean
    binIL = adsUser.InvokeGet("IsAccountLocked")
    If binIL Then
        Return True
    End If

    Return False

End Function

Private Function isDisabled(ByVal adsUser As DirectoryEntry) As Boolean
    If adsUser.InvokeGet(strAccountDisabled) Then
        Return True
    End If
    Return False
End Function

Private Function hasAccountExpired(ByVal adsUser As DirectoryEntry) As Boolean
    Dim myDate As DateTime = adsUser.InvokeGet("AccountExpirationDate")
    Dim defExpiredDate As New DateTime(1601, 1, 1)
    Dim passNeverExpiresDate As New DateTime(1970, 1, 1)
    Dim today As DateTime = Date.Now

    If (myDate.CompareTo(defExpiredDate) <> 0) And (myDate.CompareTo(passNeverExpiresDate) <> 0) Then
        If myDate.CompareTo(today) < 0 Then
            Return True
        Else
            Return False
        End If
    End If

    Return False

End Function

Private Function findUser(ByVal username As String, ByRef path As String) As Boolean
    path = ContainerFound(ContainerType.user, username)

    If path = "" Then
        Return False
    End If

    Return True
End Function

Private Function getMaxPwdAge() As String
    Using domainRoot As New DirectoryEntry(strLDAP & strADsPath, ADsUserName, ADsPassword)
        Dim maxPwdAge As LargeInteger
        Dim ticksDividerValue = -864000000000
        Dim numDays As Long
        If domainRoot.Properties.Contains(strMaxPwdAge) Then
            maxPwdAge = domainRoot.Properties(strMaxPwdAge)(0)
            numDays = ((maxPwdAge.HighPart * 2 ^ 32) + maxPwdAge.LowPart) / ticksDividerValue
        Else
            numDays = 0
        End If
        Return numDays.ToString()
    End Using
End Function

Private Function ContainerFound(ByVal Type As Byte, ByVal Item As String) As String
    'Finds the path of an object within the active directory
    Using objADObject As New DirectoryEntry

        objADObject.Path = strLDAP & strADsPath
        objADObject.Username = ADsUserName
        objADObject.Password = ADsPassword
        objADObject.AuthenticationType = AuthenticationTypes.Secure

        Using objADSearcher As New DirectorySearcher(objADObject)
            Dim Results As SearchResult
            Select Case Type
                Case ContainerType.organisationalUnit
                    objADSearcher.Filter = ("ou=" & Item)
                Case ContainerType.group, ContainerType.user
                    objADSearcher.Filter = ("cn=" & Item)
            End Select

            Try
                Results = objADSearcher.FindOne
            Catch ex As Exception
                'CannotConnectDomain
                Return ""
            End Try

            If IsNothing(Results) = True Then
                Return ""
            Else
                : Return Results.Path
            End If

        End Using
    End Using

End Function

Public Function GetDateFromLargeInteger(ByVal largeInteger As Object) As Date
    Try
        Return Date.FromFileTime(GetInt64FromLargeInteger(largeInteger))
    Catch e As ArgumentOutOfRangeException
        Throw New ArgumentException(strNotValidVal, e)
    End Try
End Function

Public Function GetInt64FromLargeInteger(ByVal largeInteger As Object) As Long
    Const highShift As Long = &H100000000
    Dim lowPart As Integer
    Dim highPart As Integer
    Dim longVal As Long
    Dim largeIntType As Type
    largeIntType = largeInteger.GetType()
    Try
        highPart = CType(largeIntType.InvokeMember("HighPart", BindingFlags.GetProperty Or BindingFlags.Public, Nothing, largeInteger, Nothing), Integer)
        lowPart = CType(largeIntType.InvokeMember("LowPart", BindingFlags.GetProperty Or BindingFlags.Public, Nothing, largeInteger, Nothing), Integer)
        longVal = (highPart * highShift) + lowPart 
        Return longVal
    Catch e As MissingMethodException
        Throw New ArgumentException(strInvalidCom, e)
    End Try
End Function

希望它能让您简要了解我们的AD检查是如何完成的。如果有帮助,请告诉我。