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.Windows.Input; using System.IO; using C4IT.LIAM; using C4IT.Logging; using C4IT.Matrix42.ServerInfo; using C4IT_IAM_Engine; using LiamWorkflowActivities; using LiamAD; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace LiamWorkflowDiagnostics { public partial class MainWindow : Window { private const int MsTeamsVisibilityPrivate = 0; private const int MsTeamsVisibilityPublic = 1; private const int MsTeamsVisibilityHiddenMembership = 2; private const int MsTeamsCloneAppsFlag = 1; private const int MsTeamsCloneTabsFlag = 2; private const int MsTeamsCloneSettingsFlag = 4; private const int MsTeamsCloneChannelsFlag = 8; private const int MsTeamsCloneMembersFlag = 16; private readonly ObservableCollection _logEntries = new ObservableCollection(); private ProviderTestSession _session; private bool _isInitializingUi; private readonly object _diagnosticsLogSync = new object(); private string _diagnosticsGuiLogPath; private string _diagnosticsStandardLogPath; private readonly string _settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LiamWorkflowDiagnostics.settings.json"); public MainWindow() { InitializeComponent(); LogListBox.ItemsSource = _logEntries; InitializeLogging(); _isInitializingUi = true; try { InitializeCombos(); MaxDepthTextBox.Text = "1"; LoadSettings(); UpdateProviderActionPanel(); ToggleActionButtons(true); } finally { _isInitializingUi = false; } 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); _diagnosticsStandardLogPath = Path.Combine(logDirectory, "LiamWorkflowDiagnostics.log"); _diagnosticsGuiLogPath = Path.Combine(logDirectory, "LiamWorkflowDiagnostics.gui.log"); cLogManagerFile.CreateInstance(_diagnosticsStandardLogPath); cLogManager.Instance.Level = LogLevels.Debug; AppendLog($"GUI-Logdatei: {_diagnosticsGuiLogPath}"); AppendLog($"Standard-Logdatei: {_diagnosticsStandardLogPath} (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; AdScopeComboBox.ItemsSource = Enum.GetValues(typeof(eLiamAccessRoleScopes)) .Cast() .Where(i => i != eLiamAccessRoleScopes.Unknown); AdScopeComboBox.SelectedItem = eLiamAccessRoleScopes.Universal; AdGroupTypeComboBox.ItemsSource = Enum.GetValues(typeof(ADServiceGroupCreator.ADGroupType)) .Cast(); AdGroupTypeComboBox.SelectedItem = ADServiceGroupCreator.ADGroupType.Distribution; MsTeamsVisibilityComboBox.ItemsSource = new[] { new MsTeamsVisibilityOption(MsTeamsVisibilityPrivate, "Private"), new MsTeamsVisibilityOption(MsTeamsVisibilityPublic, "Public"), new MsTeamsVisibilityOption(MsTeamsVisibilityHiddenMembership, "HiddenMembership") }; MsTeamsVisibilityComboBox.DisplayMemberPath = nameof(MsTeamsVisibilityOption.Label); MsTeamsVisibilityComboBox.SelectedValuePath = nameof(MsTeamsVisibilityOption.Value); MsTeamsVisibilityComboBox.SelectedValue = MsTeamsVisibilityPrivate; MsTeamsCloneSettingsCheckBox.IsChecked = true; MsTeamsCloneChannelsCheckBox.IsChecked = true; WhatIfCheckBox.IsChecked = true; 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; } if (!Dispatcher.CheckAccess()) { Dispatcher.Invoke(AddEntry); } else { AddEntry(); } try { if (!string.IsNullOrWhiteSpace(_diagnosticsGuiLogPath)) { var fileLine = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} [{level}] {message}{Environment.NewLine}"; lock (_diagnosticsLogSync) { File.AppendAllText(_diagnosticsGuiLogPath, fileLine, Encoding.UTF8); } } } 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); var session = new ProviderTestSession(msg => AppendLog(msg)); _session = session; var success = await Task.Run(() => session.InitializeAsync(providerData, maskToken, CreateProviderInstance, providerConfigClassId, providerConfigObjectId)); if (success) { AppendLog("Provider initialisiert und authentifiziert.", LogLevels.Info); ResultTextBox.Text = session.SanitizedConfigJson; } else { _session = null; AppendLog("Initialisierung fehlgeschlagen. Prüfe Log für Details.", LogLevels.Error); } } catch (Exception ex) { _session = null; 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; var providerReady = enabled && _session?.Provider != null; FetchDataAreasButton.IsEnabled = providerReady; FetchSecurityGroupsButton.IsEnabled = providerReady; ActionsGroupBox.IsEnabled = providerReady; UpdateActionHint(); } private void ProviderTypeCombo_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (_session?.Provider != null && ProviderTypeCombo.SelectedItem is eLiamProviderTypes providerType && _session.Provider.ProviderType != providerType) { _session = null; AppendLog("Provider-Typ gewechselt. Bitte Provider neu initialisieren, bevor Calls ausgeführt werden.", LogLevels.Warning); } UpdateProviderActionPanel(); ToggleActionButtons(true); if (_isInitializingUi) return; SaveSettings(); } private void ClearLogButton_Click(object sender, RoutedEventArgs e) { _logEntries.Clear(); StatusTextBlock.Text = string.Empty; AppendLog("Log gelöscht.", LogLevels.Debug); } private void WhatIfCheckBox_Click(object sender, RoutedEventArgs e) { if (_isInitializingUi) return; UpdateActionHint(); SaveSettings(); AppendLog(IsWhatIfEnabled ? "WhatIf aktiviert. Schreibende Aktionen im Diagnostics Tool werden nur simuliert." : "WhatIf deaktiviert. Schreibende Aktionen im Diagnostics Tool werden real ausgeführt.", LogLevels.Warning); } private void LogListBox_KeyDown(object sender, KeyEventArgs e) { if ((Keyboard.Modifiers & ModifierKeys.Control) != ModifierKeys.Control) return; if (e.Key == Key.A) { LogListBox.SelectAll(); e.Handled = true; return; } if (e.Key != Key.C) return; var selectedLines = LogListBox.SelectedItems .Cast() .Select(i => i?.ToString() ?? string.Empty) .Where(i => !string.IsNullOrWhiteSpace(i)) .ToList(); if (selectedLines.Count == 0) return; Clipboard.SetText(string.Join(Environment.NewLine, selectedLines)); AppendLog($"Logzeilen in Zwischenablage kopiert: {selectedLines.Count}", LogLevels.Debug); e.Handled = true; } 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; WhatIfCheckBox.IsChecked = settings.EnableWhatIf; NtfsCreateFolderPathTextBox.Text = settings.NtfsCreateFolderPath ?? string.Empty; NtfsCreateParentPathTextBox.Text = settings.NtfsCreateParentPath ?? string.Empty; NtfsCreateOwnerSidsTextBox.Text = settings.NtfsCreateOwnerSids ?? string.Empty; NtfsCreateReaderSidsTextBox.Text = settings.NtfsCreateReaderSids ?? string.Empty; NtfsCreateWriterSidsTextBox.Text = settings.NtfsCreateWriterSids ?? string.Empty; NtfsEnsureFolderPathTextBox.Text = settings.NtfsEnsureFolderPath ?? string.Empty; NtfsEnsureOwnerSidsTextBox.Text = settings.NtfsEnsureOwnerSids ?? string.Empty; NtfsEnsureReaderSidsTextBox.Text = settings.NtfsEnsureReaderSids ?? string.Empty; NtfsEnsureWriterSidsTextBox.Text = settings.NtfsEnsureWriterSids ?? string.Empty; NtfsEnsureTraverseCheckBox.IsChecked = settings.NtfsEnsureTraverse; AdServiceNameTextBox.Text = settings.AdServiceName ?? string.Empty; AdDescriptionTextBox.Text = settings.AdDescription ?? string.Empty; AdOwnerSidsTextBox.Text = settings.AdOwnerSids ?? string.Empty; AdMemberSidsTextBox.Text = settings.AdMemberSids ?? string.Empty; MsTeamsSourceTeamIdTextBox.Text = settings.MsTeamsSourceTeamId ?? string.Empty; MsTeamsNewNameTextBox.Text = settings.MsTeamsNewName ?? string.Empty; MsTeamsDescriptionTextBox.Text = settings.MsTeamsDescription ?? string.Empty; MsTeamsCloneAppsCheckBox.IsChecked = settings.MsTeamsCloneApps; MsTeamsCloneTabsCheckBox.IsChecked = settings.MsTeamsCloneTabs; MsTeamsCloneSettingsCheckBox.IsChecked = settings.MsTeamsCloneSettings; MsTeamsCloneChannelsCheckBox.IsChecked = settings.MsTeamsCloneChannels; MsTeamsCloneMembersCheckBox.IsChecked = settings.MsTeamsCloneMembers; MsTeamsAdditionalMembersTextBox.Text = settings.MsTeamsAdditionalMembers ?? string.Empty; MsTeamsAdditionalOwnersTextBox.Text = settings.MsTeamsAdditionalOwners ?? string.Empty; ExchangeMailboxNameTextBox.Text = settings.ExchangeMailboxName ?? string.Empty; ExchangeMailboxAliasTextBox.Text = settings.ExchangeMailboxAlias ?? string.Empty; ExchangeMailboxDisplayNameTextBox.Text = settings.ExchangeMailboxDisplayName ?? string.Empty; ExchangeMailboxPrimarySmtpTextBox.Text = settings.ExchangeMailboxPrimarySmtp ?? string.Empty; ExchangeDistributionNameTextBox.Text = settings.ExchangeDistributionName ?? string.Empty; ExchangeDistributionAliasTextBox.Text = settings.ExchangeDistributionAlias ?? string.Empty; ExchangeDistributionDisplayNameTextBox.Text = settings.ExchangeDistributionDisplayName ?? string.Empty; ExchangeDistributionPrimarySmtpTextBox.Text = settings.ExchangeDistributionPrimarySmtp ?? string.Empty; if (Enum.IsDefined(typeof(eLiamGroupStrategies), settings.GroupStrategy)) GroupStrategyCombo.SelectedItem = (eLiamGroupStrategies)settings.GroupStrategy; if (Enum.IsDefined(typeof(eLiamAccessRoleScopes), settings.AdScope)) AdScopeComboBox.SelectedItem = (eLiamAccessRoleScopes)settings.AdScope; if (Enum.IsDefined(typeof(ADServiceGroupCreator.ADGroupType), settings.AdGroupType)) AdGroupTypeComboBox.SelectedItem = (ADServiceGroupCreator.ADGroupType)settings.AdGroupType; MsTeamsVisibilityComboBox.SelectedValue = IsValidMsTeamsVisibility(settings.MsTeamsVisibility) ? settings.MsTeamsVisibility : MsTeamsVisibilityPrivate; } 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, EnableWhatIf = WhatIfCheckBox.IsChecked ?? true, GroupStrategy = GroupStrategyCombo.SelectedItem is eLiamGroupStrategies gs ? (int)gs : (int)eLiamGroupStrategies.Ntfs_AGDLP, NtfsCreateFolderPath = NtfsCreateFolderPathTextBox.Text ?? string.Empty, NtfsCreateParentPath = NtfsCreateParentPathTextBox.Text ?? string.Empty, NtfsCreateOwnerSids = NtfsCreateOwnerSidsTextBox.Text ?? string.Empty, NtfsCreateReaderSids = NtfsCreateReaderSidsTextBox.Text ?? string.Empty, NtfsCreateWriterSids = NtfsCreateWriterSidsTextBox.Text ?? string.Empty, NtfsEnsureFolderPath = NtfsEnsureFolderPathTextBox.Text ?? string.Empty, NtfsEnsureOwnerSids = NtfsEnsureOwnerSidsTextBox.Text ?? string.Empty, NtfsEnsureReaderSids = NtfsEnsureReaderSidsTextBox.Text ?? string.Empty, NtfsEnsureWriterSids = NtfsEnsureWriterSidsTextBox.Text ?? string.Empty, NtfsEnsureTraverse = NtfsEnsureTraverseCheckBox.IsChecked ?? false, AdServiceName = AdServiceNameTextBox.Text ?? string.Empty, AdDescription = AdDescriptionTextBox.Text ?? string.Empty, AdScope = AdScopeComboBox.SelectedItem is eLiamAccessRoleScopes scope ? (int)scope : (int)eLiamAccessRoleScopes.Universal, AdGroupType = AdGroupTypeComboBox.SelectedItem is ADServiceGroupCreator.ADGroupType groupType ? (int)groupType : (int)ADServiceGroupCreator.ADGroupType.Distribution, AdOwnerSids = AdOwnerSidsTextBox.Text ?? string.Empty, AdMemberSids = AdMemberSidsTextBox.Text ?? string.Empty, MsTeamsSourceTeamId = MsTeamsSourceTeamIdTextBox.Text ?? string.Empty, MsTeamsNewName = MsTeamsNewNameTextBox.Text ?? string.Empty, MsTeamsDescription = MsTeamsDescriptionTextBox.Text ?? string.Empty, MsTeamsVisibility = GetSelectedMsTeamsVisibility(), MsTeamsCloneApps = MsTeamsCloneAppsCheckBox.IsChecked ?? false, MsTeamsCloneTabs = MsTeamsCloneTabsCheckBox.IsChecked ?? false, MsTeamsCloneSettings = MsTeamsCloneSettingsCheckBox.IsChecked ?? true, MsTeamsCloneChannels = MsTeamsCloneChannelsCheckBox.IsChecked ?? true, MsTeamsCloneMembers = MsTeamsCloneMembersCheckBox.IsChecked ?? false, MsTeamsAdditionalMembers = MsTeamsAdditionalMembersTextBox.Text ?? string.Empty, MsTeamsAdditionalOwners = MsTeamsAdditionalOwnersTextBox.Text ?? string.Empty, ExchangeMailboxName = ExchangeMailboxNameTextBox.Text ?? string.Empty, ExchangeMailboxAlias = ExchangeMailboxAliasTextBox.Text ?? string.Empty, ExchangeMailboxDisplayName = ExchangeMailboxDisplayNameTextBox.Text ?? string.Empty, ExchangeMailboxPrimarySmtp = ExchangeMailboxPrimarySmtpTextBox.Text ?? string.Empty, ExchangeDistributionName = ExchangeDistributionNameTextBox.Text ?? string.Empty, ExchangeDistributionAlias = ExchangeDistributionAliasTextBox.Text ?? string.Empty, ExchangeDistributionDisplayName = ExchangeDistributionDisplayNameTextBox.Text ?? string.Empty, ExchangeDistributionPrimarySmtp = ExchangeDistributionPrimarySmtpTextBox.Text ?? string.Empty }; 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 void UpdateProviderActionPanel() { NtfsActionPanel.Visibility = Visibility.Collapsed; AdActionPanel.Visibility = Visibility.Collapsed; MsTeamsActionPanel.Visibility = Visibility.Collapsed; ExchangeActionPanel.Visibility = Visibility.Collapsed; if (!(ProviderTypeCombo.SelectedItem is eLiamProviderTypes providerType)) { UpdateActionHint(); return; } switch (providerType) { case eLiamProviderTypes.Ntfs: NtfsActionPanel.Visibility = Visibility.Visible; break; case eLiamProviderTypes.ActiveDirectory: AdActionPanel.Visibility = Visibility.Visible; break; case eLiamProviderTypes.MsTeams: MsTeamsActionPanel.Visibility = Visibility.Visible; break; case eLiamProviderTypes.Exchange: ExchangeActionPanel.Visibility = Visibility.Visible; break; } UpdateActionHint(); } private void UpdateActionHint() { if (!(ProviderTypeCombo.SelectedItem is eLiamProviderTypes providerType)) { ActionHintTextBlock.Text = "Waehle einen Provider-Typ aus."; return; } var providerReady = _session?.Provider != null; var providerHint = string.Empty; switch (providerType) { case eLiamProviderTypes.Ntfs: providerHint = "NTFS: neue Ordner anlegen oder auf bestehenden Ordnern fehlende Berechtigungsgruppen und ACLs additiv sicherstellen."; break; case eLiamProviderTypes.ActiveDirectory: providerHint = "Active Directory: Owner- und Member-Gruppen fuer einen Service anhand der konfigurierten Namenskonvention sicherstellen."; break; case eLiamProviderTypes.MsTeams: providerHint = "Microsoft Teams: Team-Clone mit steuerbaren Clone-Bestandteilen ausfuehren."; break; case eLiamProviderTypes.Exchange: providerHint = "Exchange: Shared Mailbox oder Distribution Group inklusive Ownership-Gruppen erzeugen."; break; default: providerHint = "Fuer diesen Provider sind keine Anlage-Aktionen hinterlegt."; break; } var modeHint = IsWhatIfEnabled ? " WhatIf ist aktiv: Schreibende Diagnostics-Aktionen werden nur simuliert." : " WhatIf ist deaktiviert: Schreibende Diagnostics-Aktionen werden real ausgeführt."; ActionHintTextBlock.Text = providerReady ? providerHint + modeHint : $"Initialisiere zuerst einen Provider. {providerHint}{modeHint}"; } private async void FetchDataAreasButton_Click(object sender, RoutedEventArgs e) { if (_session?.Provider == null) { AppendLog("Kein Provider initialisiert.", LogLevels.Warning); return; } try { var provider = _session.Provider; var configurationId = !string.IsNullOrWhiteSpace(_session.ProviderConfigObjectId) ? _session.ProviderConfigObjectId : (_session.ProviderConfigId ?? string.Empty); var runWhatIf = IsWhatIfEnabled; var maxDepth = provider.MaxDepth; AppendLog($"Lese DataAreas (MaxDepth={maxDepth}) ..."); var result = await Task.Run(() => LiamWorkflowRuntime.GetDataAreasFromProviderAsync( provider, configurationId, runWhatIf)); ResultTextBox.Text = JsonConvert.SerializeObject(result, Formatting.Indented); if (!result.Success) { AppendLog($"DataAreas-Call fehlgeschlagen [{result.ErrorCode}]: {result.ErrorMessage}", LogLevels.Error); return; } if (result.DataAreas.Count == 0) { AppendLog("Keine DataAreas gefunden.", LogLevels.Warning); return; } if (runWhatIf && result.AutomaticEnsurePreview != null && result.AutomaticEnsurePreview.Count > 0) { AppendLog($"EnsureNtfsPermissionGroups wurde nur simuliert fuer {result.AutomaticEnsurePreview.Count} Ordner. Details stehen im Result-JSON.", LogLevels.Warning); } AppendLog($"DataAreas erhalten: {result.DataAreas.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 ExecuteNtfsCreateButton_Click(object sender, RoutedEventArgs e) { try { var provider = EnsureInitializedProvider("NTFS"); var folderPath = GetRequiredText(NtfsCreateFolderPathTextBox.Text, "New Folder Path"); var parentPath = NormalizeOptionalText(NtfsCreateParentPathTextBox.Text); if (string.IsNullOrWhiteSpace(parentPath)) { parentPath = Directory.GetParent(folderPath)?.FullName; } if (string.IsNullOrWhiteSpace(parentPath)) throw new InvalidOperationException("Parent Folder Path konnte nicht ermittelt werden."); var ownerSids = ParseIdentifierList(NtfsCreateOwnerSidsTextBox.Text, "Owner SIDs"); if (ownerSids.Count == 0) throw new InvalidOperationException("Owner SIDs: mindestens ein Eintrag ist fuer die Ordneranlage erforderlich."); var readerSids = ParseIdentifierList(NtfsCreateReaderSidsTextBox.Text, "Reader SIDs"); var writerSids = ParseIdentifierList(NtfsCreateWriterSidsTextBox.Text, "Writer SIDs"); await ExecuteProviderActionAsync("NTFS Folder Create", async () => { var result = await Task.Run(() => LiamWorkflowRuntime.CreateDataAreaAsync( provider, folderPath, parentPath, null, ownerSids, readerSids, writerSids)); return new { result.Success, ResultToken = MapResultToken(result.ResultToken) }; }, () => { return CreateWhatIfResult( "NTFS Folder Create", "Wuerde einen Ordner anlegen und fehlende Gruppen sowie ACLs sicherstellen. Es wurden keine Aenderungen ausgefuehrt.", new { ProviderRootPath = provider.RootPath, NewFolderPath = folderPath, ParentFolderPath = parentPath, OwnerSids = ownerSids, ReaderSids = readerSids, WriterSids = writerSids }); }); } catch (Exception ex) { AppendLog($"NTFS Folder Create fehlgeschlagen: {ex.Message}", LogLevels.Error); MessageBox.Show(this, ex.ToString(), "NTFS Folder Create", MessageBoxButton.OK, MessageBoxImage.Error); } } private async void ExecuteNtfsEnsureButton_Click(object sender, RoutedEventArgs e) { try { var provider = EnsureInitializedProvider("NTFS"); var folderPath = GetRequiredText(NtfsEnsureFolderPathTextBox.Text, "Folder Path"); var ownerSids = ParseIdentifierList(NtfsEnsureOwnerSidsTextBox.Text, "Owner SIDs"); var readerSids = ParseIdentifierList(NtfsEnsureReaderSidsTextBox.Text, "Reader SIDs"); var writerSids = ParseIdentifierList(NtfsEnsureWriterSidsTextBox.Text, "Writer SIDs"); var ensureTraverse = NtfsEnsureTraverseCheckBox.IsChecked ?? false; await ExecuteProviderActionAsync("NTFS Ensure Groups / ACLs", async () => { var result = await Task.Run(() => LiamWorkflowRuntime.EnsureNtfsPermissionGroupsAsync( provider, folderPath, null, ownerSids, readerSids, writerSids, ensureTraverse)); return new { result.Success, ResultToken = MapResultToken(result.ResultToken) }; }, () => { return CreateWhatIfResult( "NTFS Ensure Groups / ACLs", "Wuerde fehlende NTFS-Berechtigungsgruppen und ACLs additiv sicherstellen. Es wurden keine Aenderungen ausgefuehrt.", new { ProviderRootPath = provider.RootPath, FolderPath = folderPath, OwnerSids = ownerSids, ReaderSids = readerSids, WriterSids = writerSids, EnsureTraverseGroups = ensureTraverse }); }); } catch (Exception ex) { AppendLog($"NTFS Ensure Groups / ACLs fehlgeschlagen: {ex.Message}", LogLevels.Error); MessageBox.Show(this, ex.ToString(), "NTFS Ensure Groups / ACLs", MessageBoxButton.OK, MessageBoxImage.Error); } } private async void ExecuteAdCreateButton_Click(object sender, RoutedEventArgs e) { try { var provider = EnsureInitializedProvider("Active Directory"); var serviceName = GetRequiredText(AdServiceNameTextBox.Text, "Service Name"); var description = NormalizeOptionalText(AdDescriptionTextBox.Text); var scope = AdScopeComboBox.SelectedItem is eLiamAccessRoleScopes selectedScope ? selectedScope : eLiamAccessRoleScopes.Universal; var groupType = AdGroupTypeComboBox.SelectedItem is ADServiceGroupCreator.ADGroupType selectedType ? selectedType : ADServiceGroupCreator.ADGroupType.Distribution; var ownerSids = ParseIdentifierList(AdOwnerSidsTextBox.Text, "Owner SIDs"); var memberSids = ParseIdentifierList(AdMemberSidsTextBox.Text, "Member SIDs"); await ExecuteProviderActionAsync("AD Ensure Service Groups", async () => { var result = await Task.Run(() => LiamWorkflowRuntime.CreateAdServiceGroups( provider, serviceName, description, scope, groupType, ownerSids, memberSids)); return result; }, () => { return CreateWhatIfResult( "AD Ensure Service Groups", "Wuerde Service-Gruppen im Active Directory anhand der Namenskonvention sicherstellen. Es wurden keine Aenderungen ausgefuehrt.", new { ServiceName = serviceName, Description = description, Scope = scope.ToString(), GroupType = groupType.ToString(), OwnerSids = ownerSids, MemberSids = memberSids }); }); } catch (Exception ex) { AppendLog($"AD Ensure Service Groups fehlgeschlagen: {ex.Message}", LogLevels.Error); MessageBox.Show(this, ex.ToString(), "AD Ensure Service Groups", MessageBoxButton.OK, MessageBoxImage.Error); } } private async void ExecuteMsTeamsCloneButton_Click(object sender, RoutedEventArgs e) { try { var provider = EnsureInitializedProvider("MsTeams"); var sourceTeamId = GetRequiredText(MsTeamsSourceTeamIdTextBox.Text, "Source Team ID"); var newTeamName = GetRequiredText(MsTeamsNewNameTextBox.Text, "New Team Name"); var description = NormalizeOptionalText(MsTeamsDescriptionTextBox.Text); var visibility = GetSelectedMsTeamsVisibility(); var partsToClone = GetSelectedCloneParts(); var additionalMembers = ParseIdentifierList(MsTeamsAdditionalMembersTextBox.Text, "Additional Members"); var additionalOwners = ParseIdentifierList(MsTeamsAdditionalOwnersTextBox.Text, "Additional Owners"); await ExecuteProviderActionAsync("MsTeams Clone Team", async () => { var result = await Task.Run(() => LiamWorkflowRuntime.CloneTeamAsync( provider, sourceTeamId, newTeamName, description, visibility, partsToClone, string.Join(";", additionalMembers), string.Join(";", additionalOwners))); return result; }, () => { return CreateWhatIfResult( "MsTeams Clone Team", "Wuerde ein Team anhand der gewaehlten Clone-Bestandteile klonen. Es wurden keine Aenderungen ausgefuehrt.", new { SourceTeamId = sourceTeamId, NewTeamName = newTeamName, Description = description, Visibility = visibility, PartsToClone = partsToClone, AdditionalMembers = additionalMembers, AdditionalOwners = additionalOwners }); }); } catch (Exception ex) { AppendLog($"MsTeams Clone Team fehlgeschlagen: {ex.Message}", LogLevels.Error); MessageBox.Show(this, ex.ToString(), "MsTeams Clone Team", MessageBoxButton.OK, MessageBoxImage.Error); } } private async void ExecuteExchangeMailboxButton_Click(object sender, RoutedEventArgs e) { try { var provider = EnsureInitializedProvider("Exchange"); var name = GetRequiredText(ExchangeMailboxNameTextBox.Text, "Name"); var alias = GetRequiredText(ExchangeMailboxAliasTextBox.Text, "Alias"); var displayName = NormalizeOptionalText(ExchangeMailboxDisplayNameTextBox.Text); var primarySmtp = NormalizeOptionalText(ExchangeMailboxPrimarySmtpTextBox.Text); await ExecuteProviderActionAsync("Exchange Create Shared Mailbox", async () => { var result = await Task.Run(() => LiamWorkflowRuntime.CreateSharedMailbox( provider, name, alias, displayName, primarySmtp)); return result; }, () => { return CreateWhatIfResult( "Exchange Create Shared Mailbox", "Wuerde eine Shared Mailbox inklusive Ownership-Gruppen erzeugen. Es wurden keine Aenderungen ausgefuehrt.", new { Name = name, Alias = alias, DisplayName = displayName, PrimarySmtpAddress = primarySmtp }); }); } catch (Exception ex) { AppendLog($"Exchange Create Shared Mailbox fehlgeschlagen: {ex.Message}", LogLevels.Error); MessageBox.Show(this, ex.ToString(), "Exchange Create Shared Mailbox", MessageBoxButton.OK, MessageBoxImage.Error); } } private async void ExecuteExchangeDistributionButton_Click(object sender, RoutedEventArgs e) { try { var provider = EnsureInitializedProvider("Exchange"); var name = GetRequiredText(ExchangeDistributionNameTextBox.Text, "Name"); var alias = GetRequiredText(ExchangeDistributionAliasTextBox.Text, "Alias"); var displayName = NormalizeOptionalText(ExchangeDistributionDisplayNameTextBox.Text); var primarySmtp = NormalizeOptionalText(ExchangeDistributionPrimarySmtpTextBox.Text); await ExecuteProviderActionAsync("Exchange Create Distribution Group", async () => { var result = await Task.Run(() => LiamWorkflowRuntime.CreateDistributionGroup( provider, name, alias, displayName, primarySmtp)); return result; }, () => { return CreateWhatIfResult( "Exchange Create Distribution Group", "Wuerde eine Distribution Group inklusive Ownership-Gruppen erzeugen. Es wurden keine Aenderungen ausgefuehrt.", new { Name = name, Alias = alias, DisplayName = displayName, PrimarySmtpAddress = primarySmtp }); }); } catch (Exception ex) { AppendLog($"Exchange Create Distribution Group fehlgeschlagen: {ex.Message}", LogLevels.Error); MessageBox.Show(this, ex.ToString(), "Exchange Create Distribution Group", 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 { var provider = _session.Provider; var filter = provider.GroupFilter; AppendLog($"Lese SecurityGroups (Filter='{filter}') ..."); var result = await Task.Run(() => LiamWorkflowRuntime.GetSecurityGroupsFromProviderAsync(provider)); ResultTextBox.Text = JsonConvert.SerializeObject(result, Formatting.Indented); if (!result.Success) { AppendLog($"SecurityGroups-Call fehlgeschlagen [{result.ErrorCode}]: {result.ErrorMessage}", LogLevels.Error); return; } if (result.SecurityGroups.Count == 0) { AppendLog("Keine SecurityGroups gefunden.", LogLevels.Warning); return; } AppendLog($"SecurityGroups erhalten: {result.SecurityGroups.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 async Task ExecuteProviderActionAsync(string actionName, Func> action, Func whatIfAction = null) { if (action == null) throw new ArgumentNullException(nameof(action)); ToggleActionButtons(false); try { SaveSettings(); var runInWhatIfMode = IsWhatIfEnabled && whatIfAction != null; AppendLog(runInWhatIfMode ? $"{actionName} im WhatIf-Modus gestartet. Schreibende Aenderungen werden nur simuliert." : $"{actionName} gestartet."); var result = runInWhatIfMode ? await Task.Run(whatIfAction) : await action(); ResultTextBox.Text = JsonConvert.SerializeObject(result, Formatting.Indented); if (TryGetSuccessFlag(result, out var success) && !success) AppendLog($"{actionName} mit Fehlerstatus abgeschlossen.", LogLevels.Warning); else if (runInWhatIfMode) AppendLog($"{actionName} im WhatIf-Modus simuliert abgeschlossen.", LogLevels.Warning); else AppendLog($"{actionName} erfolgreich abgeschlossen."); } catch (Exception ex) { AppendLog($"{actionName} fehlgeschlagen: {ex.Message}", LogLevels.Error); MessageBox.Show(this, ex.ToString(), actionName, MessageBoxButton.OK, MessageBoxImage.Error); } finally { ToggleActionButtons(true); } } private bool TryGetSuccessFlag(object instance, out bool success) { success = false; if (instance == null) return false; var property = instance.GetType().GetProperty("Success", BindingFlags.Instance | BindingFlags.Public); if (property == null || property.PropertyType != typeof(bool)) return false; var rawValue = property.GetValue(instance); if (!(rawValue is bool boolValue)) return false; success = boolValue; return true; } private bool IsWhatIfEnabled => WhatIfCheckBox.IsChecked ?? true; private object CreateWhatIfResult(string actionName, string message, object input) { return new { Success = true, WhatIf = true, Operation = actionName, Message = message ?? string.Empty, TimestampUtc = DateTime.UtcNow.ToString("s") + "Z", Input = input }; } private TProvider EnsureInitializedProvider(string providerName) where TProvider : cLiamProviderBase { if (_session?.Provider == null) throw new InvalidOperationException("Bitte zuerst den Provider initialisieren."); if (!(_session.Provider is TProvider provider)) throw new InvalidOperationException($"Der initialisierte Provider ist nicht vom Typ '{providerName}'. Bitte Einstellungen pruefen und neu initialisieren."); return provider; } private List ParseIdentifierList(string text, string context) { if (string.IsNullOrWhiteSpace(text)) return new List(); var trimmed = text.Trim(); if (trimmed.StartsWith("[", StringComparison.Ordinal)) { try { var array = JArray.Parse(trimmed); return array.Values() .Where(i => !string.IsNullOrWhiteSpace(i)) .Select(i => i.Trim()) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); } catch (Exception ex) { throw new InvalidOperationException($"{context}: JSON-Array konnte nicht gelesen werden. {ex.Message}"); } } return trimmed .Split(new[] { '\r', '\n', ';', ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(i => i.Trim()) .Where(i => !string.IsNullOrWhiteSpace(i)) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); } private string GetRequiredText(string value, string fieldName) { var trimmed = NormalizeOptionalText(value); if (string.IsNullOrWhiteSpace(trimmed)) throw new InvalidOperationException($"{fieldName} ist erforderlich."); return trimmed; } private string NormalizeOptionalText(string value) { var trimmed = value?.Trim(); return string.IsNullOrWhiteSpace(trimmed) ? null : trimmed; } private int GetSelectedCloneParts() { var parts = 0; if (MsTeamsCloneAppsCheckBox.IsChecked ?? false) parts |= MsTeamsCloneAppsFlag; if (MsTeamsCloneTabsCheckBox.IsChecked ?? false) parts |= MsTeamsCloneTabsFlag; if (MsTeamsCloneSettingsCheckBox.IsChecked ?? false) parts |= MsTeamsCloneSettingsFlag; if (MsTeamsCloneChannelsCheckBox.IsChecked ?? false) parts |= MsTeamsCloneChannelsFlag; if (MsTeamsCloneMembersCheckBox.IsChecked ?? false) parts |= MsTeamsCloneMembersFlag; return parts; } private object MapResultToken(ResultToken token) { if (token == null) return null; return new { Function = token.resultFunction ?? string.Empty, Message = token.resultMessage ?? string.Empty, ErrorId = token.resultErrorId, CreatedGroups = token.createdGroups ?? new List(), ReusedGroups = token.reusedGroups ?? new List(), AddedAclEntries = token.addedAclEntries ?? new List(), SkippedAclEntries = token.skippedAclEntries ?? new List(), EnsuredTraverseGroups = token.ensuredTraverseGroups ?? new List(), Warnings = token.warnings ?? new List() }; } private int GetSelectedMsTeamsVisibility() { var selectedValue = MsTeamsVisibilityComboBox.SelectedValue; if (selectedValue is int intValue && IsValidMsTeamsVisibility(intValue)) return intValue; if (selectedValue != null && int.TryParse(selectedValue.ToString(), out var parsedValue) && IsValidMsTeamsVisibility(parsedValue)) return parsedValue; return MsTeamsVisibilityPrivate; } private bool IsValidMsTeamsVisibility(int value) { return value == MsTeamsVisibilityPrivate || value == MsTeamsVisibilityPublic || value == MsTeamsVisibilityHiddenMembership; } 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 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; UpdateProviderActionPanel(); 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; public bool EnableWhatIf { get; set; } = true; public string NtfsCreateFolderPath { get; set; } = string.Empty; public string NtfsCreateParentPath { get; set; } = string.Empty; public string NtfsCreateOwnerSids { get; set; } = string.Empty; public string NtfsCreateReaderSids { get; set; } = string.Empty; public string NtfsCreateWriterSids { get; set; } = string.Empty; public string NtfsEnsureFolderPath { get; set; } = string.Empty; public string NtfsEnsureOwnerSids { get; set; } = string.Empty; public string NtfsEnsureReaderSids { get; set; } = string.Empty; public string NtfsEnsureWriterSids { get; set; } = string.Empty; public bool NtfsEnsureTraverse { get; set; } = false; public string AdServiceName { get; set; } = string.Empty; public string AdDescription { get; set; } = string.Empty; public int AdScope { get; set; } = (int)eLiamAccessRoleScopes.Universal; public int AdGroupType { get; set; } = (int)ADServiceGroupCreator.ADGroupType.Distribution; public string AdOwnerSids { get; set; } = string.Empty; public string AdMemberSids { get; set; } = string.Empty; public string MsTeamsSourceTeamId { get; set; } = string.Empty; public string MsTeamsNewName { get; set; } = string.Empty; public string MsTeamsDescription { get; set; } = string.Empty; public int MsTeamsVisibility { get; set; } = MsTeamsVisibilityPrivate; public bool MsTeamsCloneApps { get; set; } = false; public bool MsTeamsCloneTabs { get; set; } = false; public bool MsTeamsCloneSettings { get; set; } = true; public bool MsTeamsCloneChannels { get; set; } = true; public bool MsTeamsCloneMembers { get; set; } = false; public string MsTeamsAdditionalMembers { get; set; } = string.Empty; public string MsTeamsAdditionalOwners { get; set; } = string.Empty; public string ExchangeMailboxName { get; set; } = string.Empty; public string ExchangeMailboxAlias { get; set; } = string.Empty; public string ExchangeMailboxDisplayName { get; set; } = string.Empty; public string ExchangeMailboxPrimarySmtp { get; set; } = string.Empty; public string ExchangeDistributionName { get; set; } = string.Empty; public string ExchangeDistributionAlias { get; set; } = string.Empty; public string ExchangeDistributionDisplayName { get; set; } = string.Empty; public string ExchangeDistributionPrimarySmtp { get; set; } = string.Empty; } private sealed class MsTeamsVisibilityOption { public MsTeamsVisibilityOption(int value, string label) { Value = value; Label = label; } public int Value { get; } public string Label { get; } } } }