Verbesserung der Fehlerbehandlung und Protokollierung
Neue Konstanten und Wiederholungslogik in `ActiveDirectoryInterface.vb` hinzugefügt, um vorübergehende AD-Fehler zu handhaben. Funktion `IsTransientAdException` implementiert. Verbesserte Benutzer-Synchronisierungslogik in `SyncUsers.MSSQL.vb`, um unnötige Löschvorgänge zu vermeiden. Versionsnummern in `AssemblyInfo.vb` aktualisiert. Warnmeldungen und Index-Typ-Überprüfung in `Windream.vb` verbessert. NLog-Verwaltung in `Windream.vbproj` auf NuGet umgestellt und entsprechende Referenz aus `packages.config` entfernt.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
Imports System.DirectoryServices
|
||||
Imports System.DirectoryServices.AccountManagement
|
||||
Imports System.Runtime.InteropServices
|
||||
Imports System.Text.RegularExpressions
|
||||
Imports System.Threading
|
||||
Imports DigitalData.Modules.Database
|
||||
Imports DigitalData.Modules.Logging
|
||||
|
||||
@@ -13,6 +15,9 @@ Public Class ActiveDirectoryInterface
|
||||
Private _rootNode As DirectoryEntry
|
||||
|
||||
Private Const SEARCH_LIMIT = 50000
|
||||
Private Const AD_RETRY_COUNT = 3
|
||||
Private Const AD_RETRY_DELAY_MS = 5000
|
||||
Private Const LDAP_SERVER_NOT_OPERATIONAL As Integer = &H8007203A
|
||||
|
||||
Private Const SAMACCOUNTNAME = "samaccountname"
|
||||
Private Const OBJECTCLASS = "objectClass"
|
||||
@@ -47,9 +52,7 @@ Public Class ActiveDirectoryInterface
|
||||
End Function
|
||||
|
||||
Public Function SyncUsersForGroup(GroupName As String, AttributeMappings As List(Of AttributeMapping), MSSQL As MSSQLServer, Optional Filter As String = DEFAULT_USER_FILTER) As List(Of ADUser)
|
||||
Dim oUsers As New List(Of ADUser)
|
||||
Dim oSyncedUsers As New List(Of ADUser)
|
||||
Dim oGroupId As Int64 = Nothing
|
||||
Dim oUsers As List(Of ADUser)
|
||||
|
||||
Dim oSQLSync As New SyncUsers.SyncUsersMSSQL(_logConfig, MSSQL)
|
||||
Dim oSyncedUsersMSSQL As List(Of ADUser)
|
||||
@@ -57,13 +60,18 @@ Public Class ActiveDirectoryInterface
|
||||
Try
|
||||
_logger.Debug("Fetching users from ActiveDirectory")
|
||||
oUsers = ListUsers(GroupName, AttributeMappings, Filter)
|
||||
|
||||
If oUsers Is Nothing Then
|
||||
_logger.Warn("ActiveDirectory query failed for group [{0}]. Sync skipped.", GroupName)
|
||||
Return Nothing
|
||||
End If
|
||||
|
||||
_logger.Debug("Found {0} users", oUsers.Count)
|
||||
Catch ex As Exception
|
||||
_logger.Error(ex)
|
||||
Return Nothing
|
||||
End Try
|
||||
|
||||
' Do the actual sync into MSSQL
|
||||
If MSSQL IsNot Nothing Then
|
||||
oSyncedUsersMSSQL = oSQLSync.SyncUsers(GroupName, oUsers, AttributeMappings)
|
||||
If oSyncedUsersMSSQL.Count > 0 Then
|
||||
@@ -138,88 +146,112 @@ Public Class ActiveDirectoryInterface
|
||||
Public Async Function ListUsersAsync(GroupName As String, Optional Filter As String = DEFAULT_USER_FILTER) As Task(Of List(Of ADUser))
|
||||
Return Await Task.Run(Function() ListUsers(GroupName, Filter))
|
||||
End Function
|
||||
|
||||
Public Function ListUsers(GroupName As String, AttributeMappings As List(Of AttributeMapping), Optional Filter As String = DEFAULT_USER_FILTER) As List(Of ADUser)
|
||||
Dim oUsers As New List(Of ADUser)
|
||||
For oAttempt As Integer = 1 To AD_RETRY_COUNT
|
||||
Try
|
||||
Dim oUsers As New List(Of ADUser)
|
||||
|
||||
Try
|
||||
Using oContext As New PrincipalContext(ContextType.Domain)
|
||||
Using oGroupPrincipal As GroupPrincipal = GroupPrincipal.FindByIdentity(oContext, IdentityType.Name, GroupName)
|
||||
If oGroupPrincipal Is Nothing Then
|
||||
_logger.Debug("Group {0} does not exist.", GroupName)
|
||||
Return oUsers
|
||||
End If
|
||||
Using oContext As New PrincipalContext(ContextType.Domain)
|
||||
Using oGroupPrincipal As GroupPrincipal = GroupPrincipal.FindByIdentity(oContext, IdentityType.Name, GroupName)
|
||||
If oGroupPrincipal Is Nothing Then
|
||||
_logger.Debug("Group {0} does not exist.", GroupName)
|
||||
Return oUsers
|
||||
End If
|
||||
|
||||
_logger.Debug("Listing members of Group {0}", GroupName)
|
||||
_logger.Debug("Listing members of Group {0}", GroupName)
|
||||
|
||||
Using oMembers = oGroupPrincipal.GetMembers(True)
|
||||
For Each oMember As Principal In oMembers
|
||||
Try
|
||||
If TypeOf oMember Is UserPrincipal Then
|
||||
Dim oUser As UserPrincipal = DirectCast(oMember, UserPrincipal)
|
||||
Dim oUserEx As UserPrincipalEx = UserPrincipalEx.FindByIdentity(oContext, IdentityType.SamAccountName, oUser.SamAccountName)
|
||||
Dim oCustomAttributes As New List(Of ADUser.CustomAttribute)
|
||||
Using oMembers = oGroupPrincipal.GetMembers(True)
|
||||
For Each oMember As Principal In oMembers
|
||||
Try
|
||||
If TypeOf oMember Is UserPrincipal Then
|
||||
Dim oUser As UserPrincipal = DirectCast(oMember, UserPrincipal)
|
||||
Dim oUserEx As UserPrincipalEx = UserPrincipalEx.FindByIdentity(oContext, IdentityType.SamAccountName, oUser.SamAccountName)
|
||||
Dim oCustomAttributes As New List(Of ADUser.CustomAttribute)
|
||||
|
||||
Dim oUserFound = FindUserWithFilter(oUserEx, Filter)
|
||||
Dim oUserFound = FindUserWithFilter(oUserEx, Filter)
|
||||
|
||||
If oUserFound = False Then
|
||||
_logger.Debug("User [{0}] was skipped out due to user filter.", oUserEx.SamAccountName)
|
||||
Continue For
|
||||
If oUserFound = False Then
|
||||
_logger.Debug("User [{0}] was skipped out due to user filter.", oUserEx.SamAccountName)
|
||||
Continue For
|
||||
End If
|
||||
|
||||
If oUserEx IsNot Nothing Then
|
||||
For Each oMap As AttributeMapping In AttributeMappings
|
||||
Dim oAttributeValue = oUserEx.GetAttributeValue(oMap.AttributeName)
|
||||
|
||||
If oAttributeValue <> String.Empty Then
|
||||
oCustomAttributes.Add(New ADUser.CustomAttribute() With {
|
||||
.Name = oMap.AttributeName,
|
||||
.Value = oAttributeValue,
|
||||
.FirebirdSyskey = oMap.FirebirdSyskey,
|
||||
.MSSQLColumn = oMap.MSSQLColumn
|
||||
})
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
|
||||
oUsers.Add(New ADUser With {
|
||||
.SId = oUser.Sid,
|
||||
.samAccountName = oUser.SamAccountName,
|
||||
.Middlename = oUser.MiddleName,
|
||||
.GivenName = oUser.GivenName,
|
||||
.Surname = oUser.Surname,
|
||||
.GUID = oUser.Guid,
|
||||
.Email = oUser.EmailAddress,
|
||||
.CustomAttributes = oCustomAttributes
|
||||
})
|
||||
End If
|
||||
_logger.Debug("User [{0}] passed the filter.", oUserEx.SamAccountName)
|
||||
|
||||
' TODO: Figure out why oUserEx can be nothing for certain users
|
||||
If oUserEx IsNot Nothing Then
|
||||
For Each oMap As AttributeMapping In AttributeMappings
|
||||
Dim oAttributeValue = oUserEx.GetAttributeValue(oMap.AttributeName)
|
||||
|
||||
If oAttributeValue <> String.Empty Then
|
||||
_logger.Debug("Attribute [{0}] is not empty.", oMap.AttributeName)
|
||||
|
||||
oCustomAttributes.Add(New ADUser.CustomAttribute() With {
|
||||
.Name = oMap.AttributeName,
|
||||
.Value = oAttributeValue,
|
||||
.FirebirdSyskey = oMap.FirebirdSyskey,
|
||||
.MSSQLColumn = oMap.MSSQLColumn
|
||||
})
|
||||
Else
|
||||
_logger.Debug("Attribute [{0}] is empty.", oMap.AttributeName)
|
||||
End If
|
||||
Next
|
||||
Else
|
||||
_logger.Debug("Could not fetch CustomAttributes for user [{0}]", oUser)
|
||||
End If
|
||||
|
||||
_logger.Debug("Trying to add User [{0}] to user list", oUser)
|
||||
|
||||
Dim oNewUser As New ADUser With {
|
||||
.SId = oUser.Sid,
|
||||
.samAccountName = oUser.SamAccountName,
|
||||
.Middlename = oUser.MiddleName,
|
||||
.GivenName = oUser.GivenName,
|
||||
.Surname = oUser.Surname,
|
||||
.GUID = oUser.Guid,
|
||||
.Email = oUser.EmailAddress,
|
||||
.CustomAttributes = oCustomAttributes
|
||||
}
|
||||
|
||||
oUsers.Add(oNewUser)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
_logger.Warn("User could not be processed")
|
||||
_logger.Error(ex)
|
||||
End Try
|
||||
Next
|
||||
Catch ex As Exception
|
||||
_logger.Warn("User could not be processed")
|
||||
_logger.Error(ex)
|
||||
End Try
|
||||
Next
|
||||
End Using
|
||||
End Using
|
||||
End Using
|
||||
End Using
|
||||
|
||||
Return oUsers
|
||||
Catch ex As Exception
|
||||
_logger.Error(ex)
|
||||
Return oUsers
|
||||
End Try
|
||||
Return oUsers
|
||||
Catch ex As Exception
|
||||
If IsTransientAdException(ex) AndAlso oAttempt < AD_RETRY_COUNT Then
|
||||
_logger.Warn("ActiveDirectory unavailable (attempt {0}/{1}) for group [{2}]. Retrying in {3} ms.", oAttempt, AD_RETRY_COUNT, GroupName, AD_RETRY_DELAY_MS)
|
||||
Thread.Sleep(AD_RETRY_DELAY_MS)
|
||||
Continue For
|
||||
End If
|
||||
|
||||
_logger.Error(ex)
|
||||
|
||||
If IsTransientAdException(ex) Then
|
||||
_logger.Warn("ActiveDirectory unavailable after {0} attempts for group [{1}]. Returning Nothing.", AD_RETRY_COUNT, GroupName)
|
||||
Return Nothing
|
||||
End If
|
||||
|
||||
Return New List(Of ADUser)
|
||||
End Try
|
||||
Next
|
||||
|
||||
Return Nothing
|
||||
End Function
|
||||
Private Function IsTransientAdException(ex As Exception) As Boolean
|
||||
Dim oCurrent = ex
|
||||
|
||||
While oCurrent IsNot Nothing
|
||||
If TypeOf oCurrent Is COMException Then
|
||||
Dim oComEx = DirectCast(oCurrent, COMException)
|
||||
If oComEx.ErrorCode = LDAP_SERVER_NOT_OPERATIONAL Then
|
||||
Return True
|
||||
End If
|
||||
End If
|
||||
|
||||
If oCurrent.Message IsNot Nothing AndAlso oCurrent.Message.IndexOf("The server is not operational", StringComparison.OrdinalIgnoreCase) >= 0 Then
|
||||
Return True
|
||||
End If
|
||||
|
||||
oCurrent = oCurrent.InnerException
|
||||
End While
|
||||
|
||||
Return False
|
||||
End Function
|
||||
|
||||
Private Function GetPartFromFirstOU(dnString As String) As String
|
||||
Dim keyword As String = "OU="
|
||||
Dim index As Integer = dnString.IndexOf(keyword)
|
||||
|
||||
Reference in New Issue
Block a user