diff --git a/LiamWorkflowDiagnostics/MainWindow.xaml.cs b/LiamWorkflowDiagnostics/MainWindow.xaml.cs index 70ecbec..ba9fffc 100644 --- a/LiamWorkflowDiagnostics/MainWindow.xaml.cs +++ b/LiamWorkflowDiagnostics/MainWindow.xaml.cs @@ -1,88 +1,88 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.IO; -using C4IT.LIAM; -using C4IT.Logging; -using C4IT.Matrix42.ServerInfo; -using LiamWorkflowActivities; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace LiamWorkflowDiagnostics -{ - public partial class MainWindow : Window - { - private readonly ObservableCollection _logEntries = new ObservableCollection(); - private ProviderTestSession _session; - private readonly string _settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LiamWorkflowDiagnostics.settings.json"); - - public MainWindow() - { - InitializeComponent(); - LogListBox.ItemsSource = _logEntries; - InitializeLogging(); - InitializeCombos(); - MaxDepthTextBox.Text = "1"; - LoadSettings(); - this.Closing += MainWindow_Closing; - AppendLog("Diagnostics tool bereit."); - } - - private void InitializeLogging() - { - try - { - var baseDir = AppDomain.CurrentDomain.BaseDirectory; - var logDirectory = Path.Combine(baseDir, "logs"); - Directory.CreateDirectory(logDirectory); - var logPath = Path.Combine(logDirectory, "LiamWorkflowDiagnostics.log"); - cLogManagerFile.CreateInstance(logPath); - cLogManager.Instance.Level = LogLevels.Debug; - AppendLog($"Logdatei: {logPath} (Level: Debug)"); - } - catch (Exception ex) - { - AppendLog($"Konnte Logging nicht initialisieren: {ex.Message}", LogLevels.Warning); - } - } - - private void InitializeCombos() - { - var providerTypes = new[] - { - eLiamProviderTypes.MsTeams, - eLiamProviderTypes.Ntfs, - eLiamProviderTypes.ActiveDirectory, - eLiamProviderTypes.Exchange - }; - ProviderTypeCombo.ItemsSource = providerTypes; - ProviderTypeCombo.SelectedItem = eLiamProviderTypes.MsTeams; - - GroupStrategyCombo.ItemsSource = Enum.GetValues(typeof(eLiamGroupStrategies)).Cast(); - GroupStrategyCombo.SelectedItem = eLiamGroupStrategies.Ntfs_AGDLP; - - FetchDataAreasButton.IsEnabled = false; - FetchSecurityGroupsButton.IsEnabled = false; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.IO; +using C4IT.LIAM; +using C4IT.Logging; +using C4IT.Matrix42.ServerInfo; +using LiamWorkflowActivities; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace LiamWorkflowDiagnostics +{ + public partial class MainWindow : Window + { + private readonly ObservableCollection _logEntries = new ObservableCollection(); + private ProviderTestSession _session; + private readonly string _settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LiamWorkflowDiagnostics.settings.json"); + + public MainWindow() + { + InitializeComponent(); + LogListBox.ItemsSource = _logEntries; + InitializeLogging(); + InitializeCombos(); + MaxDepthTextBox.Text = "1"; + LoadSettings(); + this.Closing += MainWindow_Closing; + AppendLog("Diagnostics tool bereit."); } - private void AppendLog(string message, LogLevels level = LogLevels.Info) - { - if (message == null) - { - message = string.Empty; - } - void AddEntry() - { - var timestamped = $"{DateTime.Now:HH:mm:ss} {message}"; - _logEntries.Add(timestamped); - LogListBox.ScrollIntoView(timestamped); + private void InitializeLogging() + { + try + { + var baseDir = AppDomain.CurrentDomain.BaseDirectory; + var logDirectory = Path.Combine(baseDir, "logs"); + Directory.CreateDirectory(logDirectory); + var logPath = Path.Combine(logDirectory, "LiamWorkflowDiagnostics.log"); + cLogManagerFile.CreateInstance(logPath); + cLogManager.Instance.Level = LogLevels.Debug; + AppendLog($"Logdatei: {logPath} (Level: Debug)"); + } + catch (Exception ex) + { + AppendLog($"Konnte Logging nicht initialisieren: {ex.Message}", LogLevels.Warning); + } + } + + private void InitializeCombos() + { + var providerTypes = new[] + { + eLiamProviderTypes.MsTeams, + eLiamProviderTypes.Ntfs, + eLiamProviderTypes.ActiveDirectory, + eLiamProviderTypes.Exchange + }; + ProviderTypeCombo.ItemsSource = providerTypes; + ProviderTypeCombo.SelectedItem = eLiamProviderTypes.MsTeams; + + GroupStrategyCombo.ItemsSource = Enum.GetValues(typeof(eLiamGroupStrategies)).Cast(); + GroupStrategyCombo.SelectedItem = eLiamGroupStrategies.Ntfs_AGDLP; + + FetchDataAreasButton.IsEnabled = false; + FetchSecurityGroupsButton.IsEnabled = false; + } + + private void AppendLog(string message, LogLevels level = LogLevels.Info) + { + if (message == null) + { + message = string.Empty; + } + void AddEntry() + { + var timestamped = $"{DateTime.Now:HH:mm:ss} {message}"; + _logEntries.Add(timestamped); + LogListBox.ScrollIntoView(timestamped); StatusTextBlock.Text = message; } @@ -100,811 +100,812 @@ namespace LiamWorkflowDiagnostics cLogManager.DefaultLogger?.LogEntry(level, message); } catch { /* ignore */ } - } - - private void AppendLogMultiline(string text, LogLevels level = LogLevels.Info) - { - if (string.IsNullOrEmpty(text)) - return; - - foreach (var line in text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None)) - { - AppendLog(line, level); - } - } - - private async void InitializeButton_Click(object sender, RoutedEventArgs e) - { - ToggleActionButtons(false); - try - { - var providerData = BuildProviderDataFromFields(); - var maskToken = string.IsNullOrWhiteSpace(MaskTokenTextBox.Text) ? "***" : MaskTokenTextBox.Text.Trim(); - var providerConfigClassId = (ProviderConfigIdTextBox.Text ?? string.Empty).Trim(); - var providerConfigObjectId = (ProviderConfigObjectIdTextBox.Text ?? string.Empty).Trim(); - - ApplyMatrix42Environment(ServerNameTextBox.Text, UseHttpsCheckBox.IsChecked ?? false); - ApplyLicense(LicenseTextBox.Text); - - _session = new ProviderTestSession(msg => AppendLog(msg)); - var success = await _session.InitializeAsync(providerData, maskToken, CreateProviderInstance, providerConfigClassId, providerConfigObjectId); - if (success) - { - AppendLog("Provider initialisiert und authentifiziert.", LogLevels.Info); - FetchDataAreasButton.IsEnabled = true; - FetchSecurityGroupsButton.IsEnabled = true; - ResultTextBox.Text = _session.SanitizedConfigJson; - } - else - { - AppendLog("Initialisierung fehlgeschlagen. Prüfe Log für Details.", LogLevels.Error); - FetchDataAreasButton.IsEnabled = false; - FetchSecurityGroupsButton.IsEnabled = false; - } - } - catch (Exception ex) - { - AppendLog($"Fehler bei Initialisierung: {ex.Message}", LogLevels.Error); - MessageBox.Show(this, ex.ToString(), "Initialisierung fehlgeschlagen", MessageBoxButton.OK, MessageBoxImage.Error); - } - finally - { - ToggleActionButtons(true); - } - } - - private void ToggleActionButtons(bool enabled) - { - InitializeButton.IsEnabled = enabled; - LoadJsonButton.IsEnabled = enabled; - ExportJsonButton.IsEnabled = enabled; - } - - private void ClearLogButton_Click(object sender, RoutedEventArgs e) - { - _logEntries.Clear(); - StatusTextBlock.Text = string.Empty; - AppendLog("Log gelöscht.", LogLevels.Debug); - } - - private void LoadSettings() - { - ToolSettings settings = null; - try - { - if (File.Exists(_settingsPath)) - { - var json = File.ReadAllText(_settingsPath, Encoding.UTF8); - settings = JsonConvert.DeserializeObject(json); - } - } - catch (Exception ex) - { - AppendLog($"Einstellungen konnten nicht geladen werden: {ex.Message}", LogLevels.Warning); - } - - if (settings == null) - { - settings = new ToolSettings(); - } - ApplySettingsToUi(settings); - } - - private void ApplySettingsToUi(ToolSettings settings) - { - if (settings == null) - return; - - if (Enum.IsDefined(typeof(eLiamProviderTypes), settings.ProviderType)) - ProviderTypeCombo.SelectedItem = (eLiamProviderTypes)settings.ProviderType; - - DomainTextBox.Text = settings.Domain ?? string.Empty; - RootPathTextBox.Text = settings.RootPath ?? string.Empty; - MaxDepthTextBox.Text = string.IsNullOrWhiteSpace(settings.MaxDepth) ? "1" : settings.MaxDepth; - GroupFilterTextBox.Text = settings.GroupFilter ?? string.Empty; - GroupPathTextBox.Text = settings.GroupPath ?? string.Empty; - DataAreaFilterTextBox.Text = settings.DataAreaFilter ?? string.Empty; - DataAreaRegexTextBox.Text = settings.DataAreaRegex ?? string.Empty; - GroupRegexTextBox.Text = settings.GroupRegex ?? string.Empty; - TraverseGroupTextBox.Text = settings.TraverseGroup ?? string.Empty; - OwnerGroupGlobalTextBox.Text = settings.OwnerGroupGlobal ?? string.Empty; - OwnerGroupLocalTextBox.Text = settings.OwnerGroupLocal ?? string.Empty; - WriteGroupGlobalTextBox.Text = settings.WriteGroupGlobal ?? string.Empty; - WriteGroupLocalTextBox.Text = settings.WriteGroupLocal ?? string.Empty; - ReadGroupGlobalTextBox.Text = settings.ReadGroupGlobal ?? string.Empty; - ReadGroupLocalTextBox.Text = settings.ReadGroupLocal ?? string.Empty; - CredentialUserTextBox.Text = settings.CredentialUser ?? string.Empty; - CredentialDomainTextBox.Text = settings.CredentialDomain ?? string.Empty; - MaskTokenTextBox.Text = settings.MaskToken ?? "***"; - AdditionalConfigTextBox.Text = settings.AdditionalConfiguration ?? string.Empty; - CustomTagsTextBox.Text = settings.CustomTags ?? string.Empty; - NamingConventionsTextBox.Text = settings.NamingConventions ?? string.Empty; - RawJsonTextBox.Text = settings.RawJson ?? string.Empty; - ProviderConfigIdTextBox.Text = settings.ProviderConfigClassId ?? string.Empty; - ProviderConfigObjectIdTextBox.Text = settings.ProviderConfigObjectId ?? string.Empty; - ServerNameTextBox.Text = settings.ServerName ?? string.Empty; - UseHttpsCheckBox.IsChecked = settings.UseHttps; - LicenseTextBox.Text = settings.License ?? string.Empty; - - if (Enum.IsDefined(typeof(eLiamGroupStrategies), settings.GroupStrategy)) - GroupStrategyCombo.SelectedItem = (eLiamGroupStrategies)settings.GroupStrategy; - } - - private void SaveSettings() - { - try - { - var settings = new ToolSettings - { - ProviderType = ProviderTypeCombo.SelectedItem is eLiamProviderTypes type ? (int)type : (int)eLiamProviderTypes.MsTeams, - Domain = DomainTextBox.Text ?? string.Empty, - RootPath = RootPathTextBox.Text ?? string.Empty, - MaxDepth = MaxDepthTextBox.Text ?? string.Empty, - GroupFilter = GroupFilterTextBox.Text ?? string.Empty, - GroupPath = GroupPathTextBox.Text ?? string.Empty, - DataAreaFilter = DataAreaFilterTextBox.Text ?? string.Empty, - DataAreaRegex = DataAreaRegexTextBox.Text ?? string.Empty, - GroupRegex = GroupRegexTextBox.Text ?? string.Empty, - TraverseGroup = TraverseGroupTextBox.Text ?? string.Empty, - OwnerGroupGlobal = OwnerGroupGlobalTextBox.Text ?? string.Empty, - OwnerGroupLocal = OwnerGroupLocalTextBox.Text ?? string.Empty, - WriteGroupGlobal = WriteGroupGlobalTextBox.Text ?? string.Empty, - WriteGroupLocal = WriteGroupLocalTextBox.Text ?? string.Empty, - ReadGroupGlobal = ReadGroupGlobalTextBox.Text ?? string.Empty, - ReadGroupLocal = ReadGroupLocalTextBox.Text ?? string.Empty, - CredentialUser = CredentialUserTextBox.Text ?? string.Empty, - CredentialDomain = CredentialDomainTextBox.Text ?? string.Empty, - MaskToken = MaskTokenTextBox.Text ?? "***", - AdditionalConfiguration = AdditionalConfigTextBox.Text ?? string.Empty, - CustomTags = CustomTagsTextBox.Text ?? string.Empty, - NamingConventions = NamingConventionsTextBox.Text ?? string.Empty, - RawJson = RawJsonTextBox.Text ?? string.Empty, - ProviderConfigClassId = ProviderConfigIdTextBox.Text ?? string.Empty, - ProviderConfigObjectId = ProviderConfigObjectIdTextBox.Text ?? string.Empty, - ServerName = ServerNameTextBox.Text ?? string.Empty, - UseHttps = UseHttpsCheckBox.IsChecked ?? false, - License = LicenseTextBox.Text ?? string.Empty, - GroupStrategy = GroupStrategyCombo.SelectedItem is eLiamGroupStrategies gs ? (int)gs : (int)eLiamGroupStrategies.Ntfs_AGDLP - }; - - var directory = Path.GetDirectoryName(_settingsPath); - if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) - Directory.CreateDirectory(directory); - - var json = JsonConvert.SerializeObject(settings, Formatting.Indented); - File.WriteAllText(_settingsPath, json, Encoding.UTF8); - } - catch (Exception ex) - { - AppendLog($"Einstellungen konnten nicht gespeichert werden: {ex.Message}", LogLevels.Warning); - } - } - - private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) - { - SaveSettings(); - } - - private async void FetchDataAreasButton_Click(object sender, RoutedEventArgs e) - { - if (_session?.Provider == null) - { - AppendLog("Kein Provider initialisiert.", LogLevels.Warning); - return; - } - - try - { - var maxDepth = _session.Provider.MaxDepth >= 0 ? _session.Provider.MaxDepth : 1; - AppendLog($"Lese DataAreas (MaxDepth={maxDepth}) ..."); - var areas = await _session.Provider.getDataAreasAsync(maxDepth); - if (areas == null) - { - var providerMessage = _session.Provider.GetLastErrorMessage(); - if (_session.Provider is cLiamProviderExchange exchangeProvider) - { - var code = exchangeProvider.GetLastErrorCode(); - if (string.IsNullOrWhiteSpace(code)) - code = "EXCH_GET_DATAAREAS_FAILED"; - AppendLog($"DataAreas-Call fehlgeschlagen [{code}]: {providerMessage}", LogLevels.Error); - } - else - { - AppendLog($"DataAreas-Call fehlgeschlagen: {providerMessage}", LogLevels.Error); - } - - ResultTextBox.Text = "[]"; - return; - } - - if (areas.Count == 0) - { - AppendLog("Keine DataAreas gefunden.", LogLevels.Warning); - ResultTextBox.Text = "[]"; - return; - } - - var entries = ConvertDataAreas(areas); - var json = JsonConvert.SerializeObject(entries, Formatting.Indented); - ResultTextBox.Text = json; - AppendLog($"DataAreas erhalten: {entries.Count}"); - } - catch (Exception ex) - { - AppendLog($"Fehler beim Laden der DataAreas: {ex.Message}", LogLevels.Error); - MessageBox.Show(this, ex.ToString(), "Fehler beim DataArea-Call", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private async void FetchSecurityGroupsButton_Click(object sender, RoutedEventArgs e) - { - if (_session?.Provider == null) - { - AppendLog("Kein Provider initialisiert.", LogLevels.Warning); - return; - } - - try - { - AppendLog($"Lese SecurityGroups (Filter='{_session.Provider.GroupFilter}') ..."); - var groups = await _session.Provider.getSecurityGroupsAsync(_session.Provider.GroupFilter); - if (groups == null) - { - var providerMessage = _session.Provider.GetLastErrorMessage(); - if (_session.Provider is cLiamProviderExchange exchangeProvider) - { - var code = exchangeProvider.GetLastErrorCode(); - if (string.IsNullOrWhiteSpace(code)) - code = "EXCH_GET_SECURITYGROUPS_FAILED"; - AppendLog($"SecurityGroups-Call fehlgeschlagen [{code}]: {providerMessage}", LogLevels.Error); - } - else - { - AppendLog($"SecurityGroups-Call fehlgeschlagen: {providerMessage}", LogLevels.Error); - } - - ResultTextBox.Text = "[]"; - return; - } - - if (groups.Count == 0) - { - AppendLog("Keine SecurityGroups gefunden.", LogLevels.Warning); - ResultTextBox.Text = "[]"; - return; - } - - var entries = ConvertSecurityGroups(groups); - var json = JsonConvert.SerializeObject(entries, Formatting.Indented); - ResultTextBox.Text = json; - AppendLog($"SecurityGroups erhalten: {entries.Count}"); - } - catch (Exception ex) - { - AppendLog($"Fehler beim Laden der SecurityGroups: {ex.Message}", LogLevels.Error); - MessageBox.Show(this, ex.ToString(), "Fehler beim SecurityGroup-Call", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private void LoadJsonButton_Click(object sender, RoutedEventArgs e) - { - if (string.IsNullOrWhiteSpace(RawJsonTextBox.Text)) - { - AppendLog("Kein JSON zum Laden vorhanden.", LogLevels.Warning); - return; - } - - try - { - var data = ParseProviderDataFromInput(RawJsonTextBox.Text); - if (data == null) - { - AppendLog("JSON enthielt keine gültigen Providerdaten.", LogLevels.Error); - return; - } - PopulateFields(data); - AppendLog("Providerdaten aus JSON übernommen."); - } - catch (Exception ex) - { - AppendLog($"JSON konnte nicht geparst werden: {ex.Message}", LogLevels.Error); - MessageBox.Show(this, ex.ToString(), "JSON-Fehler", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private cLiamProviderData ParseProviderDataFromInput(string input) - { - if (string.IsNullOrWhiteSpace(input)) - return null; - - try - { - var token = JToken.Parse(input); - return ParseProviderDataFromToken(token); - } - catch (JsonReaderException) - { - // Fallback: direkter Versuch, falls nur der reine Provider-Datensatz eingegeben wurde. - return JsonConvert.DeserializeObject(input); - } - } - - private cLiamProviderData ParseProviderDataFromToken(JToken token) - { - if (token == null) - return null; - - switch (token.Type) - { - case JTokenType.Object: - var obj = (JObject)token; - - SetProviderIdentifiersFromObject(obj); - - // Wenn der Export-Endpunkt verwendet wurde, liegt die Konfiguration im Feld "configuration". - var configurationProperty = obj.Properties() - .FirstOrDefault(p => string.Equals(p.Name, "configuration", StringComparison.OrdinalIgnoreCase)); - if (configurationProperty != null) - { - var configToken = configurationProperty.Value; - var provider = configToken?.ToObject(); - if (provider == null) - return null; - - var sanitizedProperty = obj.Properties() - .FirstOrDefault(p => string.Equals(p.Name, "sanitizedJson", StringComparison.OrdinalIgnoreCase)); - if (sanitizedProperty?.Value != null && sanitizedProperty.Value.Type == JTokenType.String) - { - var sanitized = sanitizedProperty.Value.Value(); - if (!string.IsNullOrWhiteSpace(sanitized)) - RawJsonTextBox.Text = sanitized; - } - else - { - RawJsonTextBox.Text = JsonConvert.SerializeObject(provider, Formatting.Indented); - } - - return provider; - } - - // Alternativ: ein Objekt mit direkt eingebettetem Sanitized JSON. - var sanitizedJsonProperty = obj.Properties() - .FirstOrDefault(p => string.Equals(p.Name, "sanitizedJson", StringComparison.OrdinalIgnoreCase)); - if (sanitizedJsonProperty?.Value != null) - { - var sanitized = sanitizedJsonProperty.Value.Type == JTokenType.String - ? sanitizedJsonProperty.Value.Value() - : sanitizedJsonProperty.Value.ToString(); - - if (!string.IsNullOrWhiteSpace(sanitized)) - { - RawJsonTextBox.Text = sanitized; - return JsonConvert.DeserializeObject(sanitized); - } - } - - // Wenn nichts davon zutrifft, versuchen wir das Objekt direkt zu konvertieren. - var direct = obj.ToObject(); - if (direct != null) - RawJsonTextBox.Text = JsonConvert.SerializeObject(direct, Formatting.Indented); - return direct; - - case JTokenType.String: - var json = token.Value(); - if (string.IsNullOrWhiteSpace(json)) - return null; - RawJsonTextBox.Text = json; - return JsonConvert.DeserializeObject(json); - - default: - return token.ToObject(); - } - } - - private void SetProviderIdentifiersFromObject(JObject obj) - { - if (obj == null) - return; - - var classIdProp = obj.Properties().FirstOrDefault(p => string.Equals(p.Name, "providerconfigclassid", StringComparison.OrdinalIgnoreCase)); - if (classIdProp?.Value != null) - { - var value = classIdProp.Value.Type == JTokenType.String ? classIdProp.Value.Value() : classIdProp.Value.ToString(); - ProviderConfigIdTextBox.Text = value?.Trim(); - } - - var objectIdProp = obj.Properties().FirstOrDefault(p => string.Equals(p.Name, "providerconfigobjectid", StringComparison.OrdinalIgnoreCase)); - if (objectIdProp?.Value != null) - { - var value = objectIdProp.Value.Type == JTokenType.String ? objectIdProp.Value.Value() : objectIdProp.Value.ToString(); - ProviderConfigObjectIdTextBox.Text = value?.Trim(); - } - } - - private void ExportJsonButton_Click(object sender, RoutedEventArgs e) - { - try - { - var providerData = BuildProviderDataFromFields(); - var maskToken = string.IsNullOrWhiteSpace(MaskTokenTextBox.Text) ? "***" : MaskTokenTextBox.Text.Trim(); - var sanitized = ProviderTestSession.BuildSanitizedJson(providerData, maskToken); - ResultTextBox.Text = sanitized; - AppendLog("Provider-Konfiguration als sanitisiertes JSON exportiert."); - } - catch (Exception ex) - { - AppendLog($"Export fehlgeschlagen: {ex.Message}", LogLevels.Error); - MessageBox.Show(this, ex.ToString(), "Export-Fehler", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private cLiamProviderBase CreateProviderInstance(cLiamProviderData data) - { - if (data == null) - throw new ArgumentNullException(nameof(data)); - - data.ReplaceCustomTags(); - var configuration = new cLiamConfiguration(); - - switch (data.ProviderType) - { - case eLiamProviderTypes.MsTeams: - return new cLiamProviderMsTeams(configuration, data); - case eLiamProviderTypes.Ntfs: - return new cLiamProviderNtfs(configuration, data); - case eLiamProviderTypes.ActiveDirectory: - return new cLiamProviderAD(configuration, data); - case eLiamProviderTypes.Exchange: - return new cLiamProviderExchange(configuration, data); - default: - throw new NotSupportedException($"ProviderType '{data.ProviderType}' wird nicht unterstützt."); - } - } - - private cLiamProviderData BuildProviderDataFromFields() - { - if (!(ProviderTypeCombo.SelectedItem is eLiamProviderTypes providerType) || providerType == eLiamProviderTypes.Unknown) - throw new InvalidOperationException("Bitte einen Provider-Typ auswählen."); - - var additional = ParseKeyValueLines(AdditionalConfigTextBox.Text, "Additional Configuration"); - var customTags = ParseKeyValueLines(CustomTagsTextBox.Text, "Custom Tags"); - var namingConventions = ParseNamingConventions(NamingConventionsTextBox.Text); - - var password = CredentialPasswordBox.Password ?? string.Empty; - var credentialDomain = string.IsNullOrWhiteSpace(CredentialDomainTextBox.Text) - ? DomainTextBox.Text?.Trim() - : CredentialDomainTextBox.Text.Trim(); - - if (string.IsNullOrWhiteSpace(password)) - throw new InvalidOperationException("Bitte ein Kennwort für den Logon hinterlegen."); - - var data = new cLiamProviderData - { - ProviderType = providerType, - Domain = DomainTextBox.Text?.Trim() ?? string.Empty, - RootPath = RootPathTextBox.Text?.Trim() ?? string.Empty, - MaxDepth = ParseInt(MaxDepthTextBox.Text, 1), - GroupFilter = GroupFilterTextBox.Text?.Trim() ?? string.Empty, - GroupPath = GroupPathTextBox.Text?.Trim() ?? string.Empty, - GroupStrategy = GroupStrategyCombo.SelectedItem is eLiamGroupStrategies gs ? gs : eLiamGroupStrategies.None, - DataAreaFilter = DataAreaFilterTextBox.Text?.Trim() ?? string.Empty, - DataAreaRegEx = DataAreaRegexTextBox.Text?.Trim() ?? string.Empty, - GroupRegEx = GroupRegexTextBox.Text?.Trim() ?? string.Empty, - TraverseGroup = TraverseGroupTextBox.Text?.Trim() ?? string.Empty, - OwnerGroupGlobal = OwnerGroupGlobalTextBox.Text?.Trim() ?? string.Empty, - OwnerGroupLocal = OwnerGroupLocalTextBox.Text?.Trim() ?? string.Empty, - WriteGroupGlobal = WriteGroupGlobalTextBox.Text?.Trim() ?? string.Empty, - WriteGroupLocal = WriteGroupLocalTextBox.Text?.Trim() ?? string.Empty, - ReadGroupGlobal = ReadGroupGlobalTextBox.Text?.Trim() ?? string.Empty, - ReadGroupLocal = ReadGroupLocalTextBox.Text?.Trim() ?? string.Empty, - Credential = new cLiamCredential - { - Domain = credentialDomain ?? string.Empty, - Identification = CredentialUserTextBox.Text?.Trim() ?? string.Empty, - Secret = password - }, - AdditionalConfiguration = additional, - CustomTags = customTags, - NamingConventions = namingConventions - }; - - if (string.IsNullOrWhiteSpace(data.Credential.Identification)) - throw new InvalidOperationException("Bitte ein Konto für den Logon hinterlegen."); - - SaveSettings(); - - return data; - } - - private Dictionary ParseKeyValueLines(string text, string context) - { - var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - if (string.IsNullOrWhiteSpace(text)) - return result; - - var lines = text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); - foreach (var line in lines) - { - var trimmed = line.Trim(); - if (trimmed.Length == 0 || trimmed.StartsWith("#")) - continue; - - var parts = trimmed.Split(new[] { '=' }, 2); - if (parts.Length != 2) - throw new InvalidOperationException($"{context}: Ungültige Zeile '{line}'. Verwende Format key=value."); - - result[parts[0].Trim()] = parts[1].Trim(); - } - - return result; - } - - private List ParseNamingConventions(string text) - { - if (string.IsNullOrWhiteSpace(text)) - return new List(); - - try - { - return JsonConvert.DeserializeObject>(text) ?? new List(); - } - catch (Exception ex) - { - throw new InvalidOperationException($"Naming Conventions JSON ungültig: {ex.Message}"); - } - } - - private List ConvertDataAreas(IEnumerable dataAreas) - { - var result = new List(); - foreach (var dataArea in dataAreas ?? Enumerable.Empty()) - { - var ntfs = dataArea as cLiamNtfsFolder; - var adGroup = dataArea as cLiamAdGroupAsDataArea; - var exchMailbox = dataArea as cLiamExchangeSharedMailbox; - var exchDistribution = dataArea as cLiamExchangeDistributionGroup; - - var owner = exchMailbox?.OwnerGroupIdentifier - ?? exchDistribution?.OwnerGroupIdentifier - ?? adGroup?.ManagedBySID - ?? ntfs?.OwnerGroupIdentifier - ?? string.Empty; - - var write = exchMailbox != null - ? exchMailbox.FullAccessGroupSid - : exchDistribution != null - ? exchDistribution.MemberGroupSid - : adGroup?.UID - ?? ntfs?.WriteGroupIdentifier - ?? string.Empty; - - var read = exchMailbox != null - ? exchMailbox.SendAsGroupSid - : ntfs?.ReadGroupIdentifier - ?? string.Empty; - - var traverse = ntfs?.TraverseGroupIdentifier ?? string.Empty; - var created = ntfs?.CreatedDate ?? string.Empty; - var description = adGroup?.Description ?? string.Empty; - - result.Add(new DataAreaEntry - { - DisplayName = dataArea.DisplayName ?? string.Empty, - UID = dataArea.UID ?? string.Empty, - TechnicalName = dataArea.TechnicalName ?? string.Empty, - Description = description, - TargetType = ((int)dataArea.Provider.ProviderType).ToString(), - ParentUID = dataArea.ParentUID ?? string.Empty, - Level = dataArea.Level.ToString(), - Owner = owner, - Write = write, - Read = read, - Traverse = traverse, - CreatedDate = created, - ConfigurationId = !string.IsNullOrWhiteSpace(_session?.ProviderConfigObjectId) - ? _session.ProviderConfigObjectId - : (!string.IsNullOrWhiteSpace(_session?.ProviderConfigId) ? _session.ProviderConfigId : string.Empty), - BaseFolder = ntfs?.Share?.TechnicalName ?? dataArea.Provider?.RootPath ?? string.Empty, - UniqueId = dataArea.UID ?? string.Empty, - DataAreaType = ((int)dataArea.DataType).ToString() - }); - } - return result; - } - - private List ConvertSecurityGroups(IEnumerable groups) - { - var result = new List(); - foreach (var sg in groups ?? Enumerable.Empty()) - { - var entry = new SecurityGroupEntry - { - DisplayName = sg.TechnicalName ?? sg.DisplayName ?? string.Empty, - TechnicalName = sg.UID ?? string.Empty, - TargetType = ((int)sg.Provider.ProviderType).ToString() - }; - - switch (sg) - { - case cLiamAdGroup adGroup: - entry.UID = adGroup.dn; - entry.Scope = adGroup.scope; - break; - case cLiamAdGroup2 adGroup2: - entry.UID = adGroup2.dn; - entry.Scope = adGroup2.scope; - break; - case cLiamExchangeSecurityGroup exchangeGroup: - entry.UID = exchangeGroup.dn; - break; - default: - entry.UID = sg.UID; - break; - } - - result.Add(entry); - } - return result; - } - - private void PopulateFields(cLiamProviderData data) - { - if (data == null) - return; - - if (Enum.IsDefined(typeof(eLiamProviderTypes), data.ProviderType)) - ProviderTypeCombo.SelectedItem = data.ProviderType; - - DomainTextBox.Text = data.Domain ?? string.Empty; - RootPathTextBox.Text = data.RootPath ?? string.Empty; - MaxDepthTextBox.Text = data.MaxDepth.ToString(); - GroupFilterTextBox.Text = data.GroupFilter ?? string.Empty; - GroupPathTextBox.Text = data.GroupPath ?? string.Empty; - DataAreaFilterTextBox.Text = data.DataAreaFilter ?? string.Empty; - DataAreaRegexTextBox.Text = data.DataAreaRegEx ?? string.Empty; - GroupRegexTextBox.Text = data.GroupRegEx ?? string.Empty; - TraverseGroupTextBox.Text = data.TraverseGroup ?? string.Empty; - OwnerGroupGlobalTextBox.Text = data.OwnerGroupGlobal ?? string.Empty; - OwnerGroupLocalTextBox.Text = data.OwnerGroupLocal ?? string.Empty; - WriteGroupGlobalTextBox.Text = data.WriteGroupGlobal ?? string.Empty; - WriteGroupLocalTextBox.Text = data.WriteGroupLocal ?? string.Empty; - ReadGroupGlobalTextBox.Text = data.ReadGroupGlobal ?? string.Empty; - ReadGroupLocalTextBox.Text = data.ReadGroupLocal ?? string.Empty; - - if (Enum.IsDefined(typeof(eLiamGroupStrategies), data.GroupStrategy)) - GroupStrategyCombo.SelectedItem = data.GroupStrategy; - - CredentialUserTextBox.Text = data.Credential?.Identification ?? string.Empty; - CredentialDomainTextBox.Text = data.Credential?.Domain ?? string.Empty; - CredentialPasswordBox.Password = string.Empty; - - AdditionalConfigTextBox.Text = FormatKeyValueLines(data.AdditionalConfiguration); - CustomTagsTextBox.Text = FormatKeyValueLines(data.CustomTags); - NamingConventionsTextBox.Text = data.NamingConventions != null && data.NamingConventions.Count > 0 - ? JsonConvert.SerializeObject(data.NamingConventions, Formatting.Indented) - : string.Empty; - - SaveSettings(); - } - - private string FormatKeyValueLines(Dictionary source) - { - if (source == null || source.Count == 0) - return string.Empty; - - var builder = new StringBuilder(); - foreach (var kvp in source) - { - builder.Append(kvp.Key); - builder.Append('='); - builder.AppendLine(kvp.Value); - } - return builder.ToString(); - } - - private int ParseInt(string value, int fallback) - { - if (int.TryParse(value, out var result)) - return result; - return fallback; - } - - private void ApplyLicense(string licenseRaw) - { - if (string.IsNullOrWhiteSpace(licenseRaw)) - { - AppendLog("Keine Lizenz angegeben. Lizenzprüfung könnte fehlschlagen.", LogLevels.Warning); - return; - } - - try - { - if (cC4ITLicenseM42ESM.Instance == null) - _ = new cC4ITLicenseM42ESM(new Guid(cLIAMM42BaseActivity.LiamProductGuid)); - - cC4ITLicenseM42ESM.Instance.LoadFromString(licenseRaw); - AppendLog($"Lizenz geladen. Gültig: {cC4ITLicenseM42ESM.Instance.IsValid}", cC4ITLicenseM42ESM.Instance.IsValid ? LogLevels.Info : LogLevels.Warning); - } - catch (Exception ex) - { - AppendLog($"Lizenz konnte nicht geladen werden: {ex.Message}", LogLevels.Error); - throw; - } - } - - private void ApplyMatrix42Environment(string serverName, bool useHttps) - { - var trimmed = serverName?.Trim(); - if (string.IsNullOrEmpty(trimmed)) - { - AppendLog("Kein Portal-Server angegeben. Lizenz muss ggf. \"*\" enthalten.", LogLevels.Warning); - return; - } - - try - { - var instance = cMatrix42ServerInfo.DefaultInstance; - if (instance == null) - { - var type = typeof(cMatrix42ServerInfo); - instance = (cMatrix42ServerInfo)Activator.CreateInstance(type, nonPublic: true); - SetProperty(type, instance, "InstallationPath", string.Empty); - } - - var lowered = trimmed.ToLowerInvariant(); - SetProperty(instance.GetType(), instance, "ServerName", lowered); - SetProperty(instance.GetType(), instance, "IsHttps", useHttps); - - var defaultProp = typeof(cMatrix42ServerInfo).GetProperty("DefaultInstance", BindingFlags.Static | BindingFlags.Public); - var setter = defaultProp?.GetSetMethod(true); - setter?.Invoke(null, new object[] { instance }); - - AppendLog($"Matrix42 ServerInfo gesetzt auf '{lowered}' (HTTPS={(useHttps ? "ja" : "nein")})."); - } - catch (Exception ex) - { - AppendLog($"Matrix42 ServerInfo konnte nicht gesetzt werden: {ex.Message}", LogLevels.Warning); - } - } - - private void SetProperty(Type type, object instance, string propertyName, object value) - { - var prop = type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - var setter = prop?.GetSetMethod(true); - setter?.Invoke(instance, new[] { value }); - } - - private class ToolSettings - { - public int ProviderType { get; set; } = (int)eLiamProviderTypes.MsTeams; - public int GroupStrategy { get; set; } = (int)eLiamGroupStrategies.Ntfs_AGDLP; - public string Domain { get; set; } = string.Empty; - public string RootPath { get; set; } = string.Empty; - public string MaxDepth { get; set; } = "1"; - public string GroupFilter { get; set; } = string.Empty; - public string GroupPath { get; set; } = string.Empty; - public string DataAreaFilter { get; set; } = string.Empty; - public string DataAreaRegex { get; set; } = string.Empty; - public string GroupRegex { get; set; } = string.Empty; - public string TraverseGroup { get; set; } = string.Empty; - public string OwnerGroupGlobal { get; set; } = string.Empty; - public string OwnerGroupLocal { get; set; } = string.Empty; - public string WriteGroupGlobal { get; set; } = string.Empty; - public string WriteGroupLocal { get; set; } = string.Empty; - public string ReadGroupGlobal { get; set; } = string.Empty; - public string ReadGroupLocal { get; set; } = string.Empty; - public string CredentialUser { get; set; } = string.Empty; - public string CredentialDomain { get; set; } = string.Empty; - public string MaskToken { get; set; } = "***"; - public string AdditionalConfiguration { get; set; } = string.Empty; - public string CustomTags { get; set; } = string.Empty; - public string NamingConventions { get; set; } = string.Empty; - public string RawJson { get; set; } = string.Empty; - public string ProviderConfigClassId { get; set; } = string.Empty; - public string ProviderConfigObjectId { get; set; } = string.Empty; - public string ServerName { get; set; } = string.Empty; - public bool UseHttps { get; set; } = false; - public string License { get; set; } = string.Empty; - } - } -} + } + + private void AppendLogMultiline(string text, LogLevels level = LogLevels.Info) + { + if (string.IsNullOrEmpty(text)) + return; + + foreach (var line in text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None)) + { + AppendLog(line, level); + } + } + + private async void InitializeButton_Click(object sender, RoutedEventArgs e) + { + ToggleActionButtons(false); + try + { + var providerData = BuildProviderDataFromFields(); + var maskToken = string.IsNullOrWhiteSpace(MaskTokenTextBox.Text) ? "***" : MaskTokenTextBox.Text.Trim(); + var providerConfigClassId = (ProviderConfigIdTextBox.Text ?? string.Empty).Trim(); + var providerConfigObjectId = (ProviderConfigObjectIdTextBox.Text ?? string.Empty).Trim(); + + ApplyMatrix42Environment(ServerNameTextBox.Text, UseHttpsCheckBox.IsChecked ?? false); + ApplyLicense(LicenseTextBox.Text); + + _session = new ProviderTestSession(msg => AppendLog(msg)); + var success = await _session.InitializeAsync(providerData, maskToken, CreateProviderInstance, providerConfigClassId, providerConfigObjectId); + if (success) + { + AppendLog("Provider initialisiert und authentifiziert.", LogLevels.Info); + FetchDataAreasButton.IsEnabled = true; + FetchSecurityGroupsButton.IsEnabled = true; + ResultTextBox.Text = _session.SanitizedConfigJson; + } + else + { + AppendLog("Initialisierung fehlgeschlagen. Prüfe Log für Details.", LogLevels.Error); + FetchDataAreasButton.IsEnabled = false; + FetchSecurityGroupsButton.IsEnabled = false; + } + } + catch (Exception ex) + { + AppendLog($"Fehler bei Initialisierung: {ex.Message}", LogLevels.Error); + MessageBox.Show(this, ex.ToString(), "Initialisierung fehlgeschlagen", MessageBoxButton.OK, MessageBoxImage.Error); + } + finally + { + ToggleActionButtons(true); + } + } + + private void ToggleActionButtons(bool enabled) + { + InitializeButton.IsEnabled = enabled; + LoadJsonButton.IsEnabled = enabled; + ExportJsonButton.IsEnabled = enabled; + } + + private void ClearLogButton_Click(object sender, RoutedEventArgs e) + { + _logEntries.Clear(); + StatusTextBlock.Text = string.Empty; + AppendLog("Log gelöscht.", LogLevels.Debug); + } + + private void LoadSettings() + { + ToolSettings settings = null; + try + { + if (File.Exists(_settingsPath)) + { + var json = File.ReadAllText(_settingsPath, Encoding.UTF8); + settings = JsonConvert.DeserializeObject(json); + } + } + catch (Exception ex) + { + AppendLog($"Einstellungen konnten nicht geladen werden: {ex.Message}", LogLevels.Warning); + } + + if (settings == null) + { + settings = new ToolSettings(); + } + ApplySettingsToUi(settings); + } + + private void ApplySettingsToUi(ToolSettings settings) + { + if (settings == null) + return; + + if (Enum.IsDefined(typeof(eLiamProviderTypes), settings.ProviderType)) + ProviderTypeCombo.SelectedItem = (eLiamProviderTypes)settings.ProviderType; + + DomainTextBox.Text = settings.Domain ?? string.Empty; + RootPathTextBox.Text = settings.RootPath ?? string.Empty; + MaxDepthTextBox.Text = string.IsNullOrWhiteSpace(settings.MaxDepth) ? "1" : settings.MaxDepth; + GroupFilterTextBox.Text = settings.GroupFilter ?? string.Empty; + GroupPathTextBox.Text = settings.GroupPath ?? string.Empty; + DataAreaFilterTextBox.Text = settings.DataAreaFilter ?? string.Empty; + DataAreaRegexTextBox.Text = settings.DataAreaRegex ?? string.Empty; + GroupRegexTextBox.Text = settings.GroupRegex ?? string.Empty; + TraverseGroupTextBox.Text = settings.TraverseGroup ?? string.Empty; + OwnerGroupGlobalTextBox.Text = settings.OwnerGroupGlobal ?? string.Empty; + OwnerGroupLocalTextBox.Text = settings.OwnerGroupLocal ?? string.Empty; + WriteGroupGlobalTextBox.Text = settings.WriteGroupGlobal ?? string.Empty; + WriteGroupLocalTextBox.Text = settings.WriteGroupLocal ?? string.Empty; + ReadGroupGlobalTextBox.Text = settings.ReadGroupGlobal ?? string.Empty; + ReadGroupLocalTextBox.Text = settings.ReadGroupLocal ?? string.Empty; + CredentialUserTextBox.Text = settings.CredentialUser ?? string.Empty; + CredentialDomainTextBox.Text = settings.CredentialDomain ?? string.Empty; + MaskTokenTextBox.Text = settings.MaskToken ?? "***"; + AdditionalConfigTextBox.Text = settings.AdditionalConfiguration ?? string.Empty; + CustomTagsTextBox.Text = settings.CustomTags ?? string.Empty; + NamingConventionsTextBox.Text = settings.NamingConventions ?? string.Empty; + RawJsonTextBox.Text = settings.RawJson ?? string.Empty; + ProviderConfigIdTextBox.Text = settings.ProviderConfigClassId ?? string.Empty; + ProviderConfigObjectIdTextBox.Text = settings.ProviderConfigObjectId ?? string.Empty; + ServerNameTextBox.Text = settings.ServerName ?? string.Empty; + UseHttpsCheckBox.IsChecked = settings.UseHttps; + LicenseTextBox.Text = settings.License ?? string.Empty; + + if (Enum.IsDefined(typeof(eLiamGroupStrategies), settings.GroupStrategy)) + GroupStrategyCombo.SelectedItem = (eLiamGroupStrategies)settings.GroupStrategy; + } + + private void SaveSettings() + { + try + { + var settings = new ToolSettings + { + ProviderType = ProviderTypeCombo.SelectedItem is eLiamProviderTypes type ? (int)type : (int)eLiamProviderTypes.MsTeams, + Domain = DomainTextBox.Text ?? string.Empty, + RootPath = RootPathTextBox.Text ?? string.Empty, + MaxDepth = MaxDepthTextBox.Text ?? string.Empty, + GroupFilter = GroupFilterTextBox.Text ?? string.Empty, + GroupPath = GroupPathTextBox.Text ?? string.Empty, + DataAreaFilter = DataAreaFilterTextBox.Text ?? string.Empty, + DataAreaRegex = DataAreaRegexTextBox.Text ?? string.Empty, + GroupRegex = GroupRegexTextBox.Text ?? string.Empty, + TraverseGroup = TraverseGroupTextBox.Text ?? string.Empty, + OwnerGroupGlobal = OwnerGroupGlobalTextBox.Text ?? string.Empty, + OwnerGroupLocal = OwnerGroupLocalTextBox.Text ?? string.Empty, + WriteGroupGlobal = WriteGroupGlobalTextBox.Text ?? string.Empty, + WriteGroupLocal = WriteGroupLocalTextBox.Text ?? string.Empty, + ReadGroupGlobal = ReadGroupGlobalTextBox.Text ?? string.Empty, + ReadGroupLocal = ReadGroupLocalTextBox.Text ?? string.Empty, + CredentialUser = CredentialUserTextBox.Text ?? string.Empty, + CredentialDomain = CredentialDomainTextBox.Text ?? string.Empty, + MaskToken = MaskTokenTextBox.Text ?? "***", + AdditionalConfiguration = AdditionalConfigTextBox.Text ?? string.Empty, + CustomTags = CustomTagsTextBox.Text ?? string.Empty, + NamingConventions = NamingConventionsTextBox.Text ?? string.Empty, + RawJson = RawJsonTextBox.Text ?? string.Empty, + ProviderConfigClassId = ProviderConfigIdTextBox.Text ?? string.Empty, + ProviderConfigObjectId = ProviderConfigObjectIdTextBox.Text ?? string.Empty, + ServerName = ServerNameTextBox.Text ?? string.Empty, + UseHttps = UseHttpsCheckBox.IsChecked ?? false, + License = LicenseTextBox.Text ?? string.Empty, + GroupStrategy = GroupStrategyCombo.SelectedItem is eLiamGroupStrategies gs ? (int)gs : (int)eLiamGroupStrategies.Ntfs_AGDLP + }; + + var directory = Path.GetDirectoryName(_settingsPath); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + Directory.CreateDirectory(directory); + + var json = JsonConvert.SerializeObject(settings, Formatting.Indented); + File.WriteAllText(_settingsPath, json, Encoding.UTF8); + } + catch (Exception ex) + { + AppendLog($"Einstellungen konnten nicht gespeichert werden: {ex.Message}", LogLevels.Warning); + } + } + + private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + SaveSettings(); + } + + private async void FetchDataAreasButton_Click(object sender, RoutedEventArgs e) + { + if (_session?.Provider == null) + { + AppendLog("Kein Provider initialisiert.", LogLevels.Warning); + return; + } + + try + { + var maxDepth = _session.Provider.MaxDepth >= 0 ? _session.Provider.MaxDepth : 1; + AppendLog($"Lese DataAreas (MaxDepth={maxDepth}) ..."); + var areas = await _session.Provider.getDataAreasAsync(maxDepth); + if (areas == null) + { + var providerMessage = _session.Provider.GetLastErrorMessage(); + if (_session.Provider is cLiamProviderExchange exchangeProvider) + { + var code = exchangeProvider.GetLastErrorCode(); + if (string.IsNullOrWhiteSpace(code)) + code = "EXCH_GET_DATAAREAS_FAILED"; + AppendLog($"DataAreas-Call fehlgeschlagen [{code}]: {providerMessage}", LogLevels.Error); + } + else + { + AppendLog($"DataAreas-Call fehlgeschlagen: {providerMessage}", LogLevels.Error); + } + + ResultTextBox.Text = "[]"; + return; + } + + if (areas.Count == 0) + { + AppendLog("Keine DataAreas gefunden.", LogLevels.Warning); + ResultTextBox.Text = "[]"; + return; + } + + var entries = ConvertDataAreas(areas); + var json = JsonConvert.SerializeObject(entries, Formatting.Indented); + ResultTextBox.Text = json; + AppendLog($"DataAreas erhalten: {entries.Count}"); + } + catch (Exception ex) + { + AppendLog($"Fehler beim Laden der DataAreas: {ex.Message}", LogLevels.Error); + MessageBox.Show(this, ex.ToString(), "Fehler beim DataArea-Call", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + + private async void FetchSecurityGroupsButton_Click(object sender, RoutedEventArgs e) + { + if (_session?.Provider == null) + { + AppendLog("Kein Provider initialisiert.", LogLevels.Warning); + return; + } + + try + { + AppendLog($"Lese SecurityGroups (Filter='{_session.Provider.GroupFilter}') ..."); + var groups = await _session.Provider.getSecurityGroupsAsync(_session.Provider.GroupFilter); + if (groups == null) + { + var providerMessage = _session.Provider.GetLastErrorMessage(); + if (_session.Provider is cLiamProviderExchange exchangeProvider) + { + var code = exchangeProvider.GetLastErrorCode(); + if (string.IsNullOrWhiteSpace(code)) + code = "EXCH_GET_SECURITYGROUPS_FAILED"; + AppendLog($"SecurityGroups-Call fehlgeschlagen [{code}]: {providerMessage}", LogLevels.Error); + } + else + { + AppendLog($"SecurityGroups-Call fehlgeschlagen: {providerMessage}", LogLevels.Error); + } + + ResultTextBox.Text = "[]"; + return; + } + + if (groups.Count == 0) + { + AppendLog("Keine SecurityGroups gefunden.", LogLevels.Warning); + ResultTextBox.Text = "[]"; + return; + } + + var entries = ConvertSecurityGroups(groups); + var json = JsonConvert.SerializeObject(entries, Formatting.Indented); + ResultTextBox.Text = json; + AppendLog($"SecurityGroups erhalten: {entries.Count}"); + } + catch (Exception ex) + { + AppendLog($"Fehler beim Laden der SecurityGroups: {ex.Message}", LogLevels.Error); + MessageBox.Show(this, ex.ToString(), "Fehler beim SecurityGroup-Call", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + + private void LoadJsonButton_Click(object sender, RoutedEventArgs e) + { + if (string.IsNullOrWhiteSpace(RawJsonTextBox.Text)) + { + AppendLog("Kein JSON zum Laden vorhanden.", LogLevels.Warning); + return; + } + + try + { + var data = ParseProviderDataFromInput(RawJsonTextBox.Text); + if (data == null) + { + AppendLog("JSON enthielt keine gültigen Providerdaten.", LogLevels.Error); + return; + } + PopulateFields(data); + AppendLog("Providerdaten aus JSON übernommen."); + } + catch (Exception ex) + { + AppendLog($"JSON konnte nicht geparst werden: {ex.Message}", LogLevels.Error); + MessageBox.Show(this, ex.ToString(), "JSON-Fehler", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + + private cLiamProviderData ParseProviderDataFromInput(string input) + { + if (string.IsNullOrWhiteSpace(input)) + return null; + + try + { + var token = JToken.Parse(input); + return ParseProviderDataFromToken(token); + } + catch (JsonReaderException) + { + // Fallback: direkter Versuch, falls nur der reine Provider-Datensatz eingegeben wurde. + return JsonConvert.DeserializeObject(input); + } + } + + private cLiamProviderData ParseProviderDataFromToken(JToken token) + { + if (token == null) + return null; + + switch (token.Type) + { + case JTokenType.Object: + var obj = (JObject)token; + + SetProviderIdentifiersFromObject(obj); + + // Wenn der Export-Endpunkt verwendet wurde, liegt die Konfiguration im Feld "configuration". + var configurationProperty = obj.Properties() + .FirstOrDefault(p => string.Equals(p.Name, "configuration", StringComparison.OrdinalIgnoreCase)); + if (configurationProperty != null) + { + var configToken = configurationProperty.Value; + var provider = configToken?.ToObject(); + if (provider == null) + return null; + + var sanitizedProperty = obj.Properties() + .FirstOrDefault(p => string.Equals(p.Name, "sanitizedJson", StringComparison.OrdinalIgnoreCase)); + if (sanitizedProperty?.Value != null && sanitizedProperty.Value.Type == JTokenType.String) + { + var sanitized = sanitizedProperty.Value.Value(); + if (!string.IsNullOrWhiteSpace(sanitized)) + RawJsonTextBox.Text = sanitized; + } + else + { + RawJsonTextBox.Text = JsonConvert.SerializeObject(provider, Formatting.Indented); + } + + return provider; + } + + // Alternativ: ein Objekt mit direkt eingebettetem Sanitized JSON. + var sanitizedJsonProperty = obj.Properties() + .FirstOrDefault(p => string.Equals(p.Name, "sanitizedJson", StringComparison.OrdinalIgnoreCase)); + if (sanitizedJsonProperty?.Value != null) + { + var sanitized = sanitizedJsonProperty.Value.Type == JTokenType.String + ? sanitizedJsonProperty.Value.Value() + : sanitizedJsonProperty.Value.ToString(); + + if (!string.IsNullOrWhiteSpace(sanitized)) + { + RawJsonTextBox.Text = sanitized; + return JsonConvert.DeserializeObject(sanitized); + } + } + + // Wenn nichts davon zutrifft, versuchen wir das Objekt direkt zu konvertieren. + var direct = obj.ToObject(); + if (direct != null) + RawJsonTextBox.Text = JsonConvert.SerializeObject(direct, Formatting.Indented); + return direct; + + case JTokenType.String: + var json = token.Value(); + if (string.IsNullOrWhiteSpace(json)) + return null; + RawJsonTextBox.Text = json; + return JsonConvert.DeserializeObject(json); + + default: + return token.ToObject(); + } + } + + private void SetProviderIdentifiersFromObject(JObject obj) + { + if (obj == null) + return; + + var classIdProp = obj.Properties().FirstOrDefault(p => string.Equals(p.Name, "providerconfigclassid", StringComparison.OrdinalIgnoreCase)); + if (classIdProp?.Value != null) + { + var value = classIdProp.Value.Type == JTokenType.String ? classIdProp.Value.Value() : classIdProp.Value.ToString(); + ProviderConfigIdTextBox.Text = value?.Trim(); + } + + var objectIdProp = obj.Properties().FirstOrDefault(p => string.Equals(p.Name, "providerconfigobjectid", StringComparison.OrdinalIgnoreCase)); + if (objectIdProp?.Value != null) + { + var value = objectIdProp.Value.Type == JTokenType.String ? objectIdProp.Value.Value() : objectIdProp.Value.ToString(); + ProviderConfigObjectIdTextBox.Text = value?.Trim(); + } + } + + private void ExportJsonButton_Click(object sender, RoutedEventArgs e) + { + try + { + var providerData = BuildProviderDataFromFields(); + var maskToken = string.IsNullOrWhiteSpace(MaskTokenTextBox.Text) ? "***" : MaskTokenTextBox.Text.Trim(); + var sanitized = ProviderTestSession.BuildSanitizedJson(providerData, maskToken); + ResultTextBox.Text = sanitized; + AppendLog("Provider-Konfiguration als sanitisiertes JSON exportiert."); + } + catch (Exception ex) + { + AppendLog($"Export fehlgeschlagen: {ex.Message}", LogLevels.Error); + MessageBox.Show(this, ex.ToString(), "Export-Fehler", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + + private cLiamProviderBase CreateProviderInstance(cLiamProviderData data) + { + if (data == null) + throw new ArgumentNullException(nameof(data)); + + data.ReplaceCustomTags(); + var configuration = new cLiamConfiguration(); + + switch (data.ProviderType) + { + case eLiamProviderTypes.MsTeams: + return new cLiamProviderMsTeams(configuration, data); + case eLiamProviderTypes.Ntfs: + return new cLiamProviderNtfs(configuration, data); + case eLiamProviderTypes.ActiveDirectory: + return new cLiamProviderAD(configuration, data); + case eLiamProviderTypes.Exchange: + return new cLiamProviderExchange(configuration, data); + default: + throw new NotSupportedException($"ProviderType '{data.ProviderType}' wird nicht unterstützt."); + } + } + + private cLiamProviderData BuildProviderDataFromFields() + { + if (!(ProviderTypeCombo.SelectedItem is eLiamProviderTypes providerType) || providerType == eLiamProviderTypes.Unknown) + throw new InvalidOperationException("Bitte einen Provider-Typ auswählen."); + + var additional = ParseKeyValueLines(AdditionalConfigTextBox.Text, "Additional Configuration"); + var customTags = ParseKeyValueLines(CustomTagsTextBox.Text, "Custom Tags"); + var namingConventions = ParseNamingConventions(NamingConventionsTextBox.Text); + + var password = CredentialPasswordBox.Password ?? string.Empty; + var credentialDomain = string.IsNullOrWhiteSpace(CredentialDomainTextBox.Text) + ? DomainTextBox.Text?.Trim() + : CredentialDomainTextBox.Text.Trim(); + + if (string.IsNullOrWhiteSpace(password)) + throw new InvalidOperationException("Bitte ein Kennwort für den Logon hinterlegen."); + + var data = new cLiamProviderData + { + ProviderType = providerType, + Domain = DomainTextBox.Text?.Trim() ?? string.Empty, + RootPath = RootPathTextBox.Text?.Trim() ?? string.Empty, + MaxDepth = ParseInt(MaxDepthTextBox.Text, 1), + GroupFilter = GroupFilterTextBox.Text?.Trim() ?? string.Empty, + GroupPath = GroupPathTextBox.Text?.Trim() ?? string.Empty, + GroupStrategy = GroupStrategyCombo.SelectedItem is eLiamGroupStrategies gs ? gs : eLiamGroupStrategies.None, + DataAreaFilter = DataAreaFilterTextBox.Text?.Trim() ?? string.Empty, + DataAreaRegEx = DataAreaRegexTextBox.Text?.Trim() ?? string.Empty, + GroupRegEx = GroupRegexTextBox.Text?.Trim() ?? string.Empty, + TraverseGroup = TraverseGroupTextBox.Text?.Trim() ?? string.Empty, + OwnerGroupGlobal = OwnerGroupGlobalTextBox.Text?.Trim() ?? string.Empty, + OwnerGroupLocal = OwnerGroupLocalTextBox.Text?.Trim() ?? string.Empty, + WriteGroupGlobal = WriteGroupGlobalTextBox.Text?.Trim() ?? string.Empty, + WriteGroupLocal = WriteGroupLocalTextBox.Text?.Trim() ?? string.Empty, + ReadGroupGlobal = ReadGroupGlobalTextBox.Text?.Trim() ?? string.Empty, + ReadGroupLocal = ReadGroupLocalTextBox.Text?.Trim() ?? string.Empty, + Credential = new cLiamCredential + { + Domain = credentialDomain ?? string.Empty, + Identification = CredentialUserTextBox.Text?.Trim() ?? string.Empty, + Secret = password + }, + AdditionalConfiguration = additional, + CustomTags = customTags, + NamingConventions = namingConventions + }; + + if (string.IsNullOrWhiteSpace(data.Credential.Identification)) + throw new InvalidOperationException("Bitte ein Konto für den Logon hinterlegen."); + + SaveSettings(); + + return data; + } + + private Dictionary ParseKeyValueLines(string text, string context) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (string.IsNullOrWhiteSpace(text)) + return result; + + var lines = text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); + foreach (var line in lines) + { + var trimmed = line.Trim(); + if (trimmed.Length == 0 || trimmed.StartsWith("#")) + continue; + + var parts = trimmed.Split(new[] { '=' }, 2); + if (parts.Length != 2) + throw new InvalidOperationException($"{context}: Ungültige Zeile '{line}'. Verwende Format key=value."); + + result[parts[0].Trim()] = parts[1].Trim(); + } + + return result; + } + + private List ParseNamingConventions(string text) + { + if (string.IsNullOrWhiteSpace(text)) + return new List(); + + try + { + return JsonConvert.DeserializeObject>(text) ?? new List(); + } + catch (Exception ex) + { + throw new InvalidOperationException($"Naming Conventions JSON ungültig: {ex.Message}"); + } + } + + private List ConvertDataAreas(IEnumerable dataAreas) + { + var result = new List(); + foreach (var dataArea in dataAreas ?? Enumerable.Empty()) + { + var ntfsPermissionArea = dataArea as cLiamNtfsPermissionDataAreaBase; + var ntfsFolder = dataArea as cLiamNtfsFolder; + var adGroup = dataArea as cLiamAdGroupAsDataArea; + var exchMailbox = dataArea as cLiamExchangeSharedMailbox; + var exchDistribution = dataArea as cLiamExchangeDistributionGroup; + + var owner = exchMailbox?.OwnerGroupIdentifier + ?? exchDistribution?.OwnerGroupIdentifier + ?? adGroup?.ManagedBySID + ?? ntfsPermissionArea?.OwnerGroupIdentifier + ?? string.Empty; + + var write = exchMailbox != null + ? exchMailbox.FullAccessGroupSid + : exchDistribution != null + ? exchDistribution.MemberGroupSid + : adGroup?.UID + ?? ntfsPermissionArea?.WriteGroupIdentifier + ?? string.Empty; + + var read = exchMailbox != null + ? exchMailbox.SendAsGroupSid + : ntfsPermissionArea?.ReadGroupIdentifier + ?? string.Empty; + + var traverse = ntfsPermissionArea?.TraverseGroupIdentifier ?? string.Empty; + var created = ntfsFolder?.CreatedDate ?? string.Empty; + var description = adGroup?.Description ?? string.Empty; + + result.Add(new DataAreaEntry + { + DisplayName = dataArea.DisplayName ?? string.Empty, + UID = dataArea.UID ?? string.Empty, + TechnicalName = dataArea.TechnicalName ?? string.Empty, + Description = description, + TargetType = ((int)dataArea.Provider.ProviderType).ToString(), + ParentUID = dataArea.ParentUID ?? string.Empty, + Level = dataArea.Level.ToString(), + Owner = owner, + Write = write, + Read = read, + Traverse = traverse, + CreatedDate = created, + ConfigurationId = !string.IsNullOrWhiteSpace(_session?.ProviderConfigObjectId) + ? _session.ProviderConfigObjectId + : (!string.IsNullOrWhiteSpace(_session?.ProviderConfigId) ? _session.ProviderConfigId : string.Empty), + BaseFolder = ntfsFolder?.Share?.TechnicalName ?? dataArea.Provider?.RootPath ?? string.Empty, + UniqueId = dataArea.UID ?? string.Empty, + DataAreaType = ((int)dataArea.DataType).ToString() + }); + } + return result; + } + + private List ConvertSecurityGroups(IEnumerable groups) + { + var result = new List(); + foreach (var sg in groups ?? Enumerable.Empty()) + { + var entry = new SecurityGroupEntry + { + DisplayName = sg.TechnicalName ?? sg.DisplayName ?? string.Empty, + TechnicalName = sg.UID ?? string.Empty, + TargetType = ((int)sg.Provider.ProviderType).ToString() + }; + + switch (sg) + { + case cLiamAdGroup adGroup: + entry.UID = adGroup.dn; + entry.Scope = adGroup.scope; + break; + case cLiamAdGroup2 adGroup2: + entry.UID = adGroup2.dn; + entry.Scope = adGroup2.scope; + break; + case cLiamExchangeSecurityGroup exchangeGroup: + entry.UID = exchangeGroup.dn; + break; + default: + entry.UID = sg.UID; + break; + } + + result.Add(entry); + } + return result; + } + + private void PopulateFields(cLiamProviderData data) + { + if (data == null) + return; + + if (Enum.IsDefined(typeof(eLiamProviderTypes), data.ProviderType)) + ProviderTypeCombo.SelectedItem = data.ProviderType; + + DomainTextBox.Text = data.Domain ?? string.Empty; + RootPathTextBox.Text = data.RootPath ?? string.Empty; + MaxDepthTextBox.Text = data.MaxDepth.ToString(); + GroupFilterTextBox.Text = data.GroupFilter ?? string.Empty; + GroupPathTextBox.Text = data.GroupPath ?? string.Empty; + DataAreaFilterTextBox.Text = data.DataAreaFilter ?? string.Empty; + DataAreaRegexTextBox.Text = data.DataAreaRegEx ?? string.Empty; + GroupRegexTextBox.Text = data.GroupRegEx ?? string.Empty; + TraverseGroupTextBox.Text = data.TraverseGroup ?? string.Empty; + OwnerGroupGlobalTextBox.Text = data.OwnerGroupGlobal ?? string.Empty; + OwnerGroupLocalTextBox.Text = data.OwnerGroupLocal ?? string.Empty; + WriteGroupGlobalTextBox.Text = data.WriteGroupGlobal ?? string.Empty; + WriteGroupLocalTextBox.Text = data.WriteGroupLocal ?? string.Empty; + ReadGroupGlobalTextBox.Text = data.ReadGroupGlobal ?? string.Empty; + ReadGroupLocalTextBox.Text = data.ReadGroupLocal ?? string.Empty; + + if (Enum.IsDefined(typeof(eLiamGroupStrategies), data.GroupStrategy)) + GroupStrategyCombo.SelectedItem = data.GroupStrategy; + + CredentialUserTextBox.Text = data.Credential?.Identification ?? string.Empty; + CredentialDomainTextBox.Text = data.Credential?.Domain ?? string.Empty; + CredentialPasswordBox.Password = string.Empty; + + AdditionalConfigTextBox.Text = FormatKeyValueLines(data.AdditionalConfiguration); + CustomTagsTextBox.Text = FormatKeyValueLines(data.CustomTags); + NamingConventionsTextBox.Text = data.NamingConventions != null && data.NamingConventions.Count > 0 + ? JsonConvert.SerializeObject(data.NamingConventions, Formatting.Indented) + : string.Empty; + + SaveSettings(); + } + + private string FormatKeyValueLines(Dictionary source) + { + if (source == null || source.Count == 0) + return string.Empty; + + var builder = new StringBuilder(); + foreach (var kvp in source) + { + builder.Append(kvp.Key); + builder.Append('='); + builder.AppendLine(kvp.Value); + } + return builder.ToString(); + } + + private int ParseInt(string value, int fallback) + { + if (int.TryParse(value, out var result)) + return result; + return fallback; + } + + private void ApplyLicense(string licenseRaw) + { + if (string.IsNullOrWhiteSpace(licenseRaw)) + { + AppendLog("Keine Lizenz angegeben. Lizenzprüfung könnte fehlschlagen.", LogLevels.Warning); + return; + } + + try + { + if (cC4ITLicenseM42ESM.Instance == null) + _ = new cC4ITLicenseM42ESM(new Guid(cLIAMM42BaseActivity.LiamProductGuid)); + + cC4ITLicenseM42ESM.Instance.LoadFromString(licenseRaw); + AppendLog($"Lizenz geladen. Gültig: {cC4ITLicenseM42ESM.Instance.IsValid}", cC4ITLicenseM42ESM.Instance.IsValid ? LogLevels.Info : LogLevels.Warning); + } + catch (Exception ex) + { + AppendLog($"Lizenz konnte nicht geladen werden: {ex.Message}", LogLevels.Error); + throw; + } + } + + private void ApplyMatrix42Environment(string serverName, bool useHttps) + { + var trimmed = serverName?.Trim(); + if (string.IsNullOrEmpty(trimmed)) + { + AppendLog("Kein Portal-Server angegeben. Lizenz muss ggf. \"*\" enthalten.", LogLevels.Warning); + return; + } + + try + { + var instance = cMatrix42ServerInfo.DefaultInstance; + if (instance == null) + { + var type = typeof(cMatrix42ServerInfo); + instance = (cMatrix42ServerInfo)Activator.CreateInstance(type, nonPublic: true); + SetProperty(type, instance, "InstallationPath", string.Empty); + } + + var lowered = trimmed.ToLowerInvariant(); + SetProperty(instance.GetType(), instance, "ServerName", lowered); + SetProperty(instance.GetType(), instance, "IsHttps", useHttps); + + var defaultProp = typeof(cMatrix42ServerInfo).GetProperty("DefaultInstance", BindingFlags.Static | BindingFlags.Public); + var setter = defaultProp?.GetSetMethod(true); + setter?.Invoke(null, new object[] { instance }); + + AppendLog($"Matrix42 ServerInfo gesetzt auf '{lowered}' (HTTPS={(useHttps ? "ja" : "nein")})."); + } + catch (Exception ex) + { + AppendLog($"Matrix42 ServerInfo konnte nicht gesetzt werden: {ex.Message}", LogLevels.Warning); + } + } + + private void SetProperty(Type type, object instance, string propertyName, object value) + { + var prop = type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + var setter = prop?.GetSetMethod(true); + setter?.Invoke(instance, new[] { value }); + } + + private class ToolSettings + { + public int ProviderType { get; set; } = (int)eLiamProviderTypes.MsTeams; + public int GroupStrategy { get; set; } = (int)eLiamGroupStrategies.Ntfs_AGDLP; + public string Domain { get; set; } = string.Empty; + public string RootPath { get; set; } = string.Empty; + public string MaxDepth { get; set; } = "1"; + public string GroupFilter { get; set; } = string.Empty; + public string GroupPath { get; set; } = string.Empty; + public string DataAreaFilter { get; set; } = string.Empty; + public string DataAreaRegex { get; set; } = string.Empty; + public string GroupRegex { get; set; } = string.Empty; + public string TraverseGroup { get; set; } = string.Empty; + public string OwnerGroupGlobal { get; set; } = string.Empty; + public string OwnerGroupLocal { get; set; } = string.Empty; + public string WriteGroupGlobal { get; set; } = string.Empty; + public string WriteGroupLocal { get; set; } = string.Empty; + public string ReadGroupGlobal { get; set; } = string.Empty; + public string ReadGroupLocal { get; set; } = string.Empty; + public string CredentialUser { get; set; } = string.Empty; + public string CredentialDomain { get; set; } = string.Empty; + public string MaskToken { get; set; } = "***"; + public string AdditionalConfiguration { get; set; } = string.Empty; + public string CustomTags { get; set; } = string.Empty; + public string NamingConventions { get; set; } = string.Empty; + public string RawJson { get; set; } = string.Empty; + public string ProviderConfigClassId { get; set; } = string.Empty; + public string ProviderConfigObjectId { get; set; } = string.Empty; + public string ServerName { get; set; } = string.Empty; + public bool UseHttps { get; set; } = false; + public string License { get; set; } = string.Empty; + } + } +}