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:
Developer01
2026-11-03 10:36:24 +01:00
parent 24417da48c
commit a6ae874873
7 changed files with 179 additions and 126 deletions

View File

@@ -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)