Files
LIAM/LiamNtfs/C4IT_IAM_SET/DataArea_FileSystem.cs
2026-03-18 15:42:54 +01:00

1314 lines
63 KiB
C#

using C4IT_IAM;
using C4IT_IAM_Engine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
using System.IO;
using System.Linq;
using System.Net;
using System.Security;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Text.RegularExpressions;
using C4IT.Logging;
using static C4IT.Logging.cLogManager;
using System.Reflection;
using C4IT_IAM_GET;
namespace C4IT_IAM_SET
{
public class DataArea_FileSystem
{
public const string constApplicationDataPath = "%ProgramData%\\Consulting4IT GmbH\\LIAM";
public string domainName;
public string username;
public SecureString password;
private cNetworkConnection Connection;
public string groupPrefix;
public string groupOUPath;
public string baseFolder;
public string newFolderPath;
public string newFolder;
public string newFolderParent;
public PermissionGroupStrategy groupPermissionStrategy;
public string groupDescriptionTemplate;
public string groupNamingTemplate;
public string groupDLTag;
public string groupGTag;
public string groupOwnerTag;
public string groupReadTag;
public string groupWriteTag;
public string groupTraverseTag;
public string groupWildcard;
public IDictionary<string, string> groupCustomTags;
public ICollection<string> ownerUserSids;
public ICollection<string> readerUserSids;
public ICollection<string> writerUserSids;
public bool forceStrictAdGroupNames;
public bool WhatIf;
public int ReadACLPermission = 0x200A9;
public int WriteACLPermission = 0x301BF;
public int OwnerACLPermission = 0x1F01FF;
public string ConfigID;
Dictionary<FileSystemRights, string> adGroupDic;
public DataArea newDataArea;
public SecurityGroups newSecurityGroups;
public List<IAM_SecurityGroupTemplate> templates;
public int createTraverseGroupLvl = 0;
public DataArea_FileSystem()
{
var logDirectory = Environment.ExpandEnvironmentVariables(constApplicationDataPath);
Helper.CreatePathWithWriteAccess(logDirectory);
var LogPath = Path.Combine(logDirectory, "Logs");
cLogManagerFile.CreateInstance(Path.Combine(LogPath, "LIAM.log"));
DefaultLogger.LogEntry(LogLevels.Info, "=================================================");
DefaultLogger.LogEntry(LogLevels.Info, $"LIAM engine v{Assembly.GetExecutingAssembly().GetName().Version} started");
templates = new List<IAM_SecurityGroupTemplate>();
}
private ResultToken checkRequiredVariables()
{
ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
resultToken.resultErrorId = 0;
if (String.IsNullOrEmpty(ConfigID))
{
resultToken.resultErrorId = 30001;
resultToken.resultMessage = "Kein ConfigID gewählt ";
return resultToken;
}
if (string.IsNullOrEmpty(username) | String.IsNullOrEmpty(new NetworkCredential("", password).Password) | String.IsNullOrEmpty(domainName))
{
resultToken.resultErrorId = 30002;
resultToken.resultMessage = "Fehlende Anmeldeinformationen";
return resultToken;
}
if (String.IsNullOrEmpty(groupPrefix))
{
resultToken.resultErrorId = 30004;
resultToken.resultMessage = "Kein Gruppen Präfix angegeben";
return resultToken;
}
if (String.IsNullOrEmpty(newFolderPath))
{
resultToken.resultErrorId = 30005;
resultToken.resultMessage = "Kein Pfad für neues Verzeichnis angegeben";
return resultToken;
}
if (String.IsNullOrEmpty(newFolderParent))
{
resultToken.resultErrorId = 30006;
resultToken.resultMessage = "Kein Pfad für neues Übergeordnetesverzeichnis angegeben";
return resultToken;
}
if (String.IsNullOrEmpty(baseFolder))
{
resultToken.resultErrorId = 30007;
resultToken.resultMessage = "Kein Basisverzeichnis angegeben";
return resultToken;
}
return resultToken;
}
public ResultToken createDataArea()
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
resultToken.resultErrorId = 0;
if (checkRequiredVariables().resultErrorId == 0)
{
InitializeFolderContext();
try
{
// ImpersonationHelper.Impersonate(domainName, username, new NetworkCredential("", password).Password, delegate
// {
if (Connection != null)
Connection.Dispose();
DefaultLogger.LogEntry(LogLevels.Info, $"Establishing connection to {baseFolder}, User: {username}, Password: {Helper.MaskAllButLastAndFirst(new NetworkCredential("", password).Password)}");
using (Connection = new cNetworkConnection(baseFolder, username, new NetworkCredential("", password).Password))
{
var folderCheckResult = checkFolder();
if (folderCheckResult.resultErrorId == 0)
{
try
{
createADGroups(resultToken);
try
{
resultToken = MergeResultTokens(resultToken, createFolder());
if (resultToken.resultErrorId == 0)
{
try
{
resultToken = MergeResultTokens(resultToken, SetTraversePermissions());
}
catch (Exception e)
{
resultToken.resultErrorId = 30200;
resultToken.resultMessage = "Fehler beim setzen der Traverserechte \n" + e.Message;
}
}
}
catch (Exception e)
{
resultToken.resultErrorId = 30200;
resultToken.resultMessage = "Fehler beim Erstellen der Verzeichnisse \n" + e.Message;
}
}
catch (Exception e)
{
resultToken.resultErrorId = 30100;
resultToken.resultMessage = "Fehler beim Erstellen der AD Gruppen \n" + e.Message;
}
}
else
{
resultToken = folderCheckResult;
}
/* },
logonType,
logonProvider);
*/
}
}
catch (Exception e)
{
resultToken.resultErrorId = 30000;
resultToken.resultMessage = "Fehler beim Herstellen der Verbindung \n " + e.Message;
}
}
else
{
return checkRequiredVariables();
}
return resultToken;
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
private ResultToken MergeResultTokens(ResultToken target, ResultToken source)
{
if (target == null)
return source;
if (source == null)
return target;
if (source.resultErrorId != 0 || target.resultErrorId == 0)
target.resultErrorId = source.resultErrorId;
if (!string.IsNullOrWhiteSpace(source.resultMessage))
target.resultMessage = source.resultMessage;
if (!string.IsNullOrWhiteSpace(source.resultFunction))
target.resultFunction = source.resultFunction;
target.createdGroups.AddRange(source.createdGroups);
target.reusedGroups.AddRange(source.reusedGroups);
target.addedAclEntries.AddRange(source.addedAclEntries);
target.skippedAclEntries.AddRange(source.skippedAclEntries);
target.ensuredTraverseGroups.AddRange(source.ensuredTraverseGroups);
target.warnings.AddRange(source.warnings);
return target;
}
private ResultToken checkRequiredVariablesForEnsure()
{
ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
resultToken.resultErrorId = 0;
if (String.IsNullOrEmpty(ConfigID))
{
resultToken.resultErrorId = 30001;
resultToken.resultMessage = "Kein ConfigID gewählt ";
return resultToken;
}
if (string.IsNullOrEmpty(username) | String.IsNullOrEmpty(new NetworkCredential("", password).Password) | String.IsNullOrEmpty(domainName))
{
resultToken.resultErrorId = 30002;
resultToken.resultMessage = "Fehlende Anmeldeinformationen";
return resultToken;
}
if (String.IsNullOrEmpty(groupPrefix))
{
resultToken.resultErrorId = 30004;
resultToken.resultMessage = "Kein Gruppen Präfix angegeben";
return resultToken;
}
if (String.IsNullOrEmpty(newFolderPath))
{
resultToken.resultErrorId = 30005;
resultToken.resultMessage = "Kein Pfad für Verzeichnis angegeben";
return resultToken;
}
if (String.IsNullOrEmpty(baseFolder))
{
resultToken.resultErrorId = 30007;
resultToken.resultMessage = "Kein Basisverzeichnis angegeben";
return resultToken;
}
return resultToken;
}
private void InitializeFolderContext()
{
newDataArea = new DataArea();
var folder = new IAM_Folder
{
configurationID = ConfigID,
technicalName = newFolderPath,
targetType = (int)IAM_TargetType.FileSystem,
Parent = newFolderParent,
ParentUID = string.IsNullOrWhiteSpace(newFolderParent) ? string.Empty : DataArea.GetUniqueDataAreaID(newFolderParent),
baseFolder = baseFolder
};
newDataArea.IAM_Folders.Add(folder);
newSecurityGroups = new SecurityGroups
{
username = username,
domainName = domainName,
password = password,
ForceStrictAdGroupNames = forceStrictAdGroupNames
};
}
public ResultToken ensureDataAreaPermissions(bool ensureTraverseGroups = false)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
var resultToken = checkRequiredVariablesForEnsure();
if (resultToken.resultErrorId != 0)
return resultToken;
if (Connection != null)
Connection.Dispose();
using (Connection = new cNetworkConnection(baseFolder, username, new NetworkCredential("", password).Password))
{
if (!Directory.Exists(newFolderPath))
{
resultToken.resultErrorId = 30203;
resultToken.resultMessage = "Verzeichnis existiert nicht";
return resultToken;
}
var parentDirectory = Directory.GetParent(newFolderPath);
if (string.IsNullOrWhiteSpace(newFolderParent))
newFolderParent = parentDirectory?.FullName;
InitializeFolderContext();
ensureADGroups(resultToken);
resultToken = ensureFolderPermissions(resultToken);
if (resultToken.resultErrorId != 0)
return resultToken;
if (ensureTraverseGroups)
{
if (WhatIf)
{
resultToken.warnings.Add("Traverse group preview is not supported in WhatIf mode for automatic DataArea ensure.");
resultToken.resultMessage = "Gruppen- und ACL-Vorschau erfolgreich erstellt";
return resultToken;
}
var traverseResult = SetTraversePermissions();
if (traverseResult != null)
{
resultToken.createdGroups.AddRange(traverseResult.createdGroups);
resultToken.reusedGroups.AddRange(traverseResult.reusedGroups);
resultToken.addedAclEntries.AddRange(traverseResult.addedAclEntries);
resultToken.skippedAclEntries.AddRange(traverseResult.skippedAclEntries);
resultToken.ensuredTraverseGroups.AddRange(traverseResult.ensuredTraverseGroups);
resultToken.warnings.AddRange(traverseResult.warnings);
if (traverseResult.resultErrorId != 0)
{
resultToken.resultErrorId = traverseResult.resultErrorId;
resultToken.resultMessage = traverseResult.resultMessage;
return resultToken;
}
}
}
resultToken.resultMessage = WhatIf
? "Gruppen- und ACL-Vorschau erfolgreich erstellt"
: "Gruppen und ACLs erfolgreich sichergestellt";
return resultToken;
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
private ResultToken SetTraversePermissions()
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
resultToken.resultErrorId = 0;
// Loggen der DomainContext-Parameter
DefaultLogger.LogEntry(LogLevels.Debug, $"DomainName: {domainName}, Username: {username}");
if (string.IsNullOrEmpty(domainName) || string.IsNullOrEmpty(username) || password is null)
{
DefaultLogger.LogEntry(LogLevels.Error, "Eines der DomainContext-Parameter ist null oder leer.");
return resultToken;
}
var domainContext = new PrincipalContext(ContextType.Domain, domainName, username, new NetworkCredential("", password).Password);
DefaultLogger.LogEntry(LogLevels.Debug, "PrincipalContext erfolgreich erstellt.");
// Überprüfen von newDataArea und IAM_Folders
if (newDataArea == null)
{
DefaultLogger.LogEntry(LogLevels.Error, "newDataArea ist null.");
return resultToken;
}
if (newDataArea.IAM_Folders == null || newDataArea.IAM_Folders.Count == 0)
{
DefaultLogger.LogEntry(LogLevels.Error, "IAM_Folders ist null oder leer.");
return resultToken;
}
DirectoryInfo newDir = new DirectoryInfo(newDataArea.IAM_Folders[0].technicalName);
DefaultLogger.LogEntry(LogLevels.Debug, $"Neues Verzeichnis: {newDir.FullName}");
DirectoryInfo parent = newDir.Parent;
if (parent == null)
{
DefaultLogger.LogEntry(LogLevels.Error, "Parent-Verzeichnis ist null.");
return resultToken;
}
DefaultLogger.LogEntry(LogLevels.Debug, $"Parent-Verzeichnis: {parent.FullName}");
var lvl = DataArea.GetRelativePath(parent.FullName, baseFolder).Count(n => n == Path.DirectorySeparatorChar);
DefaultLogger.LogEntry(LogLevels.Debug, $"Ebene (lvl): {lvl}");
// Überprüfen der Templates
if (templates == null)
{
DefaultLogger.LogEntry(LogLevels.Error, "templates ist null.");
return resultToken;
}
var traverseGroupTemplate = templates.FirstOrDefault(t => t.Type.Equals(SecurityGroupType.Traverse));
if (traverseGroupTemplate == null)
{
DefaultLogger.LogEntry(LogLevels.Error, "traverseGroupTemplate ist null.");
return resultToken;
}
DefaultLogger.LogEntry(LogLevels.Debug, $"traverseGroupTemplate gefunden");
// Überprüfen der traverseGroupTemplate-Eigenschaften
if (traverseGroupTemplate.WildcardTemplate == null)
{
DefaultLogger.LogEntry(LogLevels.Error, "WildcardTemplate von traverseGroupTemplate ist null.");
return resultToken;
}
DefaultLogger.LogEntry(LogLevels.Debug, $"traverseGroupTemplate.WildcardTemplate: {traverseGroupTemplate.WildcardTemplate}");
if (traverseGroupTemplate.NamingTemplate == null)
{
DefaultLogger.LogEntry(LogLevels.Error, "NamingTemplate von traverseGroupTemplate ist null.");
return resultToken;
}
DefaultLogger.LogEntry(LogLevels.Debug, $"traverseGroupTemplate.NamingTemplate: {traverseGroupTemplate.NamingTemplate}");
if (string.IsNullOrEmpty(baseFolder))
{
DefaultLogger.LogEntry(LogLevels.Error, "baseFolder ist null oder leer.");
return resultToken;
}
GroupPrincipal traverseGroup = null;
// Überprüfen, ob createTraverseGroupLvl initialisiert ist
DefaultLogger.LogEntry(LogLevels.Debug, $"createTraverseGroupLvl: {createTraverseGroupLvl}");
if (createTraverseGroupLvl == -1)
{
DefaultLogger.LogEntry(LogLevels.Error, "createTraverseGroupLvl ist auf -1 gesetzt.");
return resultToken;
}
for (int i = lvl; i >= createTraverseGroupLvl; i--)
{
DefaultLogger.LogEntry(LogLevels.Debug, $"Verarbeite Ebene {i}.");
if (parent == null)
{
DefaultLogger.LogEntry(LogLevels.Error, "Parent ist null innerhalb der Schleife.");
break;
}
DefaultLogger.LogEntry(LogLevels.Debug, $"Hole ACL für Ordner: {parent.FullName}");
AuthorizationRuleCollection ACLs = null;
try
{
ACLs = parent.GetAccessControl(AccessControlSections.Access).GetAccessRules(true, true, typeof(NTAccount));
DefaultLogger.LogEntry(LogLevels.Debug, "ACLs erfolgreich abgerufen.");
}
catch (Exception ex)
{
DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Abrufen der ACLs: {ex.Message}");
continue; // Weiter zur nächsten Iteration
}
if (ACLs == null)
{
DefaultLogger.LogEntry(LogLevels.Error, "ACLs ist null.");
continue;
}
GroupPrincipal parentTraverseGroup = null;
var parentTraverseAclExists = false;
string relativePathRaw = DataArea.GetRelativePath(parent.FullName, baseFolder).Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
relativePathRaw = relativePathRaw.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
DefaultLogger.LogEntry(LogLevels.Debug, $"relativePath vor Normalisierung: {relativePathRaw}");
var relativePathSegments = relativePathRaw.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
var sanitizedSegments = relativePathSegments.Select(Helper.SanitizePathSegment).ToArray();
var relativePath = sanitizedSegments.Length > 0 ? string.Join("_", sanitizedSegments) : string.Empty;
DefaultLogger.LogEntry(LogLevels.Debug, $"relativePath nach Normalisierung: {relativePath}");
var folderName = sanitizedSegments.Length > 0
? sanitizedSegments[sanitizedSegments.Length - 1]
: Helper.SanitizePathSegment(Path.GetFileName(parent.FullName.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)));
var traverseNameTemplate = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.NamingTemplate, true, relativePath, sanitizedSegments, folderName);
var traverseDescriptionTemplate = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.DescriptionTemplate, true, relativePath, sanitizedSegments, folderName);
string traverseRegex = null;
try
{
traverseRegex = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.WildcardTemplate, true, relativePath, sanitizedSegments, folderName);
DefaultLogger.LogEntry(LogLevels.Debug, $"traverseRegex: {traverseRegex}");
}
catch (Exception ex)
{
DefaultLogger.LogEntry(LogLevels.Error, $"Fehler bei der Erstellung von traverseRegex: {ex.Message}");
continue;
}
foreach (FileSystemAccessRule acl in ACLs)
{
var searchString = acl.IdentityReference.Value;
var aclSplit = searchString.Split('\\');
if (aclSplit.Length == 2)
{
searchString = aclSplit[1];
}
DefaultLogger.LogEntry(LogLevels.Debug, $"Suche GroupPrincipal für: {searchString}");
var princ = GroupPrincipal.FindByIdentity(domainContext, searchString);
if (princ != null)
{
DefaultLogger.LogEntry(LogLevels.Debug, $"Gefundene Gruppe: {princ.Name}");
}
else
{
DefaultLogger.LogEntry(LogLevels.Debug, $"Keine Gruppe gefunden für: {searchString}");
}
if (princ != null && Regex.IsMatch(princ.Name, traverseRegex, RegexOptions.IgnoreCase))
{
parentTraverseGroup = princ;
parentTraverseAclExists = true;
DefaultLogger.LogEntry(LogLevels.Debug, $"parentTraverseGroup gesetzt: {parentTraverseGroup.Name}");
}
if (parentTraverseGroup != null)
break;
}
if (parentTraverseGroup == null && !string.IsNullOrEmpty(traverseNameTemplate))
{
for (var loop = 0; loop < 20; loop++)
{
var candidateName = traverseNameTemplate.ReplaceLoopTag(loop);
parentTraverseGroup = GroupPrincipal.FindByIdentity(domainContext, candidateName);
if (parentTraverseGroup == null)
continue;
resultToken.reusedGroups.Add(candidateName);
resultToken.ensuredTraverseGroups.Add(candidateName);
DefaultLogger.LogEntry(LogLevels.Debug, $"Vorhandene Traverse-Gruppe wiederverwendet: {candidateName}");
break;
}
}
if (parentTraverseGroup == null && !traverseGroupTemplate.NamingTemplate.Equals(string.Empty))
{
DefaultLogger.LogEntry(LogLevels.Debug, "Erstelle neue TraverseGroup.");
if (newSecurityGroups == null)
{
DefaultLogger.LogEntry(LogLevels.Error, "newSecurityGroups ist null.");
continue;
}
IAM_SecurityGroup newTraverseGroup = null;
var loop = 0;
do
{
try
{
newTraverseGroup = new IAM_SecurityGroup()
{
Name = traverseNameTemplate.ReplaceLoopTag(loop),
description = traverseDescriptionTemplate.ReplaceLoopTag(loop),
technicalName = "CN=" + traverseNameTemplate.ReplaceLoopTag(loop) + "," + groupOUPath,
Scope = traverseGroupTemplate.Scope
};
DefaultLogger.LogEntry(LogLevels.Debug, $"Erstellte TraverseGroup: {newTraverseGroup.Name} (Loop: {loop})");
loop++;
}
catch (Exception ex)
{
DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Erstellen von newTraverseGroup: {ex.Message}");
break;
}
} while (newSecurityGroups.GroupAllreadyExisting(newTraverseGroup.Name.ToUpper()) && loop < 20);
if (newTraverseGroup != null)
{
if (string.IsNullOrEmpty(groupOUPath))
{
DefaultLogger.LogEntry(LogLevels.Error, "groupOUPath ist null oder leer.");
continue;
}
if (parent.Parent != null)
{
DefaultLogger.LogEntry(LogLevels.Debug, "Parent.Parent ist nicht null. Erstelle AD-Gruppe.");
if (WhatIf)
{
resultToken.createdGroups.Add(newTraverseGroup.Name);
resultToken.ensuredTraverseGroups.Add(newTraverseGroup.Name);
resultToken.warnings.Add($"Traverse-Gruppe würde angelegt werden: {newTraverseGroup.Name}");
resultToken.addedAclEntries.Add(newTraverseGroup.Name);
parentTraverseAclExists = true;
}
else
{
try
{
newSecurityGroups.CreateADGroup(groupOUPath, newTraverseGroup, null);
DefaultLogger.LogEntry(LogLevels.Debug, $"AD-Gruppe erstellt: {newTraverseGroup.Name}");
resultToken.createdGroups.Add(newTraverseGroup.Name);
resultToken.ensuredTraverseGroups.Add(newTraverseGroup.Name);
}
catch (Exception ex)
{
DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Erstellen der AD-Gruppe: {ex.Message}");
continue;
}
parentTraverseGroup = GroupPrincipal.FindByIdentity(domainContext, newTraverseGroup.Name);
if (parentTraverseGroup == null)
{
DefaultLogger.LogEntry(LogLevels.Error, $"parentTraverseGroup konnte nach Erstellung der Gruppe nicht gefunden werden: {newTraverseGroup.Name}");
continue;
}
try
{
var accesscontrol = parent.GetAccessControl();
accesscontrol.AddAccessRule(new FileSystemAccessRule(parentTraverseGroup.Sid,
FileSystemRights.Read, InheritanceFlags.None, PropagationFlags.None,
AccessControlType.Allow));
DefaultLogger.LogEntry(LogLevels.Debug, $"Setze Traverse-ACL auf: {parent.FullName} für {parentTraverseGroup.DistinguishedName}");
parent.SetAccessControl(accesscontrol);
resultToken.addedAclEntries.Add(parentTraverseGroup.Name);
parentTraverseAclExists = true;
}
catch (Exception ex)
{
DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Setzen der ACL: {ex.Message}");
continue;
}
}
}
else
{
DefaultLogger.LogEntry(LogLevels.Debug, "Parent.Parent ist null. Traverse-ACL kann nicht gesetzt werden.");
}
}
}
if (parentTraverseGroup != null && !parentTraverseAclExists)
{
try
{
var accessControl = parent.GetAccessControl();
var rules = accessControl.GetAccessRules(true, true, typeof(SecurityIdentifier)).Cast<FileSystemAccessRule>();
var hasAcl = rules.Any(rule =>
rule.AccessControlType == AccessControlType.Allow
&& rule.IdentityReference.Value == parentTraverseGroup.Sid.Value
&& (rule.FileSystemRights & FileSystemRights.Read) == FileSystemRights.Read);
if (hasAcl)
{
resultToken.skippedAclEntries.Add(parentTraverseGroup.Name);
}
else
{
if (!WhatIf)
{
accessControl.AddAccessRule(new FileSystemAccessRule(parentTraverseGroup.Sid,
FileSystemRights.Read, InheritanceFlags.None, PropagationFlags.None,
AccessControlType.Allow));
parent.SetAccessControl(accessControl);
}
resultToken.addedAclEntries.Add(parentTraverseGroup.Name);
}
}
catch (Exception ex)
{
DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Sicherstellen der Traverse-ACL: {ex.Message}");
continue;
}
}
if (parentTraverseGroup != null)
{
if (i == lvl)
{
DefaultLogger.LogEntry(LogLevels.Debug, "Verarbeite SecurityGroups bei oberster Ebene.");
foreach (var currentSecGroup in newSecurityGroups.IAM_SecurityGroups)
{
if (currentSecGroup == null)
{
DefaultLogger.LogEntry(LogLevels.Error, "currentSecGroup ist null.");
continue;
}
if (currentSecGroup.Scope != GroupScope.Global)
continue;
if (WhatIf)
{
resultToken.warnings.Add($"Traverse-Gruppe '{parentTraverseGroup.Name}' würde Mitglied '{currentSecGroup.Name}' erhalten.");
continue;
}
if (!TryEnsureGlobalGroupMembershipWithRetry(domainContext, parentTraverseGroup, currentSecGroup))
continue;
}
traverseGroup = parentTraverseGroup;
}
else
{
if (traverseGroup != null && parentTraverseGroup != null)
{
try
{
if (!parentTraverseGroup.Members.Contains(traverseGroup))
{
if (WhatIf)
{
resultToken.warnings.Add($"Traverse-Gruppe '{parentTraverseGroup.Name}' würde verschachtelte Gruppe '{traverseGroup.Name}' erhalten.");
}
else
{
if (!TryEnsureNestedTraverseGroupMembershipWithRetry(parentTraverseGroup, traverseGroup))
continue;
}
}
}
catch (Exception ex)
{
DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Hinzufügen der Traverse-Gruppe: {ex.Message}");
continue;
}
}
}
try
{
if (!WhatIf)
{
parentTraverseGroup.Save();
DefaultLogger.LogEntry(LogLevels.Debug, $"parentTraverseGroup gespeichert: {parentTraverseGroup.Name}");
}
}
catch (Exception ex)
{
DefaultLogger.LogEntry(LogLevels.Error, $"Fehler beim Speichern der parentTraverseGroup: {ex.Message}");
}
}
else
{
DefaultLogger.LogEntry(LogLevels.Debug, "parentTraverseGroup ist null.");
}
if (parentTraverseGroup != null && !resultToken.ensuredTraverseGroups.Contains(parentTraverseGroup.Name))
resultToken.ensuredTraverseGroups.Add(parentTraverseGroup.Name);
// Aktualisiere parent und lvl für die nächste Iteration
parent = parent.Parent;
if (parent != null)
{
lvl = DataArea.GetRelativePath(parent.FullName, baseFolder).Count(n => n == Path.DirectorySeparatorChar);
DefaultLogger.LogEntry(LogLevels.Debug, $"Neue Ebene (lvl) nach Aktualisierung: {lvl}");
}
else
{
DefaultLogger.LogEntry(LogLevels.Debug, "Parent nach Aktualisierung ist null.");
}
}
return resultToken;
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
private bool TryEnsureGlobalGroupMembershipWithRetry(PrincipalContext domainContext, GroupPrincipal parentTraverseGroup, IAM_SecurityGroup currentSecGroup)
{
if (domainContext == null || parentTraverseGroup == null || currentSecGroup == null || string.IsNullOrWhiteSpace(currentSecGroup.UID))
return false;
return RetryTraverseMembershipAction(
currentSecGroup.Name,
currentSecGroup.CreatedNewEntry,
() =>
{
using (var groupPrincipal = GroupPrincipal.FindByIdentity(domainContext, IdentityType.Sid, currentSecGroup.UID))
{
if (groupPrincipal == null)
return false;
if (parentTraverseGroup.Members.Contains(groupPrincipal))
return true;
DefaultLogger.LogEntry(LogLevels.Debug, $"Füge {groupPrincipal.DistinguishedName} zur Traverse-Gruppe {parentTraverseGroup.DistinguishedName} hinzu");
parentTraverseGroup.Members.Add(groupPrincipal);
parentTraverseGroup.Save();
return true;
}
});
}
private bool TryEnsureNestedTraverseGroupMembershipWithRetry(GroupPrincipal parentTraverseGroup, GroupPrincipal traverseGroup)
{
if (parentTraverseGroup == null || traverseGroup == null)
return false;
return RetryTraverseMembershipAction(
traverseGroup.Name,
true,
() =>
{
if (parentTraverseGroup.Members.Contains(traverseGroup))
return true;
DefaultLogger.LogEntry(LogLevels.Debug, $"Füge {traverseGroup.DistinguishedName} zur Traverse-Gruppe {parentTraverseGroup.DistinguishedName} hinzu");
parentTraverseGroup.Members.Add(traverseGroup);
parentTraverseGroup.Save();
return true;
});
}
private bool RetryTraverseMembershipAction(string memberName, bool allowRetry, Func<bool> tryAction)
{
if (tryAction == null)
return false;
var retryDelaysMs = allowRetry
? new[] { 0, 250, 500, 1000, 2000, 5000 }
: new[] { 0 };
var delayIndex = 0;
var maxWait = allowRetry ? TimeSpan.FromMinutes(3) : TimeSpan.Zero;
var waitStopwatch = Stopwatch.StartNew();
Exception lastException = null;
while (true)
{
var delayMs = retryDelaysMs[Math.Min(delayIndex, retryDelaysMs.Length - 1)];
if (delayMs > 0)
System.Threading.Thread.Sleep(delayMs);
if (delayIndex < retryDelaysMs.Length - 1)
delayIndex++;
try
{
if (tryAction())
{
if (waitStopwatch.ElapsedMilliseconds > 0)
DefaultLogger.LogEntry(LogLevels.Debug, $"Traverse-Mitgliedschaft für '{memberName}' nach {waitStopwatch.Elapsed.TotalSeconds:F1}s erfolgreich.");
return true;
}
}
catch (Exception ex)
{
lastException = ex;
DefaultLogger.LogEntry(LogLevels.Debug, $"Traverse-Mitgliedschaft für '{memberName}' noch nicht möglich: {ex.Message}");
}
if (!allowRetry || waitStopwatch.Elapsed >= maxWait)
break;
}
var suffix = lastException == null ? string.Empty : $" Letzte Exception: {lastException.Message}";
DefaultLogger.LogEntry(LogLevels.Warning, $"Traverse-Mitgliedschaft für '{memberName}' konnte nach {waitStopwatch.Elapsed.TotalSeconds:F1}s nicht sichergestellt werden.{suffix}");
return false;
}
private ResultToken checkFolder()
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
resultToken.resultErrorId = 0;
if (Directory.Exists(newDataArea.IAM_Folders[0].technicalName))
{
resultToken.resultMessage = "New folder " + newDataArea.IAM_Folders[0].technicalName + " already exists";
resultToken.resultErrorId = 30201;
}
return resultToken;
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
private static bool HasMatchingAccessRule(DirectoryInfo directory, SecurityIdentifier sid, FileSystemRights rights)
{
var rules = directory.GetAccessControl(AccessControlSections.Access)
.GetAccessRules(true, true, typeof(SecurityIdentifier))
.Cast<FileSystemAccessRule>();
foreach (var rule in rules)
{
if (rule.AccessControlType != AccessControlType.Allow)
continue;
if (!(rule.IdentityReference is SecurityIdentifier ruleSid))
continue;
if (!string.Equals(ruleSid.Value, sid.Value, StringComparison.OrdinalIgnoreCase))
continue;
if ((rule.FileSystemRights & rights) == rights)
return true;
}
return false;
}
private ResultToken ensureFolderPermissions(ResultToken resultToken)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
var directory = new DirectoryInfo(newDataArea.IAM_Folders[0].technicalName);
foreach (var currentSecGroup in newSecurityGroups.IAM_SecurityGroups)
{
if (WhatIf && string.IsNullOrWhiteSpace(currentSecGroup?.UID) && currentSecGroup?.CreatedNewEntry == true)
{
resultToken.addedAclEntries.Add(currentSecGroup.Name);
continue;
}
if (string.IsNullOrWhiteSpace(currentSecGroup?.UID))
{
resultToken.warnings.Add($"Keine SID für Gruppe '{currentSecGroup?.Name}' verfügbar.");
continue;
}
if (!(groupPermissionStrategy == PermissionGroupStrategy.AGDLP && currentSecGroup.Scope == GroupScope.Local
|| groupPermissionStrategy == PermissionGroupStrategy.AGP && currentSecGroup.Scope == GroupScope.Global))
{
continue;
}
var sid = new SecurityIdentifier(currentSecGroup.UID);
if (HasMatchingAccessRule(directory, sid, currentSecGroup.rights))
{
resultToken.skippedAclEntries.Add(currentSecGroup.Name);
continue;
}
if (!WhatIf)
DataArea.AddDirectorySecurity(newDataArea.IAM_Folders[0].baseFolder, newDataArea.IAM_Folders[0].technicalName, sid, currentSecGroup.rights, AccessControlType.Allow);
resultToken.addedAclEntries.Add(currentSecGroup.Name);
}
return resultToken;
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
private void ensureADGroups(ResultToken resultToken)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
newSecurityGroups.IAM_SecurityGroups.Clear();
newSecurityGroups.GenerateNewSecurityGroups(baseFolder,
newDataArea.IAM_Folders[0].technicalName,
groupPrefix,
groupOUPath,
groupPermissionStrategy,
groupTraverseTag,
groupReadTag,
groupWriteTag,
groupOwnerTag,
groupDLTag,
groupGTag,
groupCustomTags,
templates,
ReadACLPermission,
WriteACLPermission,
OwnerACLPermission,
0);
List<UserPrincipal> owners = getUserPrincipalBySid(ownerUserSids);
List<UserPrincipal> writers = getUserPrincipalBySid(writerUserSids);
List<UserPrincipal> readers = getUserPrincipalBySid(readerUserSids);
for (int i = 0; newSecurityGroups.IAM_SecurityGroups.Count > i; i++)
{
List<UserPrincipal> users;
if (newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper().EndsWith(groupOwnerTag.ToUpper()))
users = owners;
else if (newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper().EndsWith(groupWriteTag.ToUpper()))
users = writers;
else if (newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper().EndsWith(groupReadTag.ToUpper()))
users = readers;
else
users = null;
if (WhatIf)
{
var existingGroup = newSecurityGroups.PreviewADGroup(groupOUPath, newSecurityGroups.IAM_SecurityGroups[i], newDataArea.IAM_Folders[0].technicalName);
newSecurityGroups.IAM_SecurityGroups[i].CreatedNewEntry = existingGroup == null;
}
else
{
newSecurityGroups.EnsureADGroup(groupOUPath, newSecurityGroups.IAM_SecurityGroups[i], users, newDataArea.IAM_Folders[0].technicalName);
}
if (newSecurityGroups.IAM_SecurityGroups[i].CreatedNewEntry)
resultToken.createdGroups.Add(newSecurityGroups.IAM_SecurityGroups[i].Name);
else
resultToken.reusedGroups.Add(newSecurityGroups.IAM_SecurityGroups[i].Name);
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
private ResultToken createFolder()
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
ResultToken resultToken = new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString());
resultToken.resultErrorId = 0;
if (!Directory.Exists(newFolderParent))
{
resultToken.resultMessage = "Übergeordnetesverzeichnis " + newDataArea.IAM_Folders[0].Parent + " des neuen Ordners " + newDataArea.IAM_Folders[0].technicalName + " existiert nicht";
resultToken.resultErrorId = 30202;
return resultToken;
}
else
{
if (WhatIf)
{
newDataArea.IAM_Folders[0].UID = DataArea.GetUniqueDataAreaID(newDataArea.IAM_Folders[0].technicalName);
resultToken.warnings.Add($"Verzeichnis würde erstellt werden: {newDataArea.IAM_Folders[0].technicalName}");
for (int i = 0; newSecurityGroups.IAM_SecurityGroups.Count > i; i++)
{
var currentSecGroup = newSecurityGroups.IAM_SecurityGroups[i];
if (groupPermissionStrategy == PermissionGroupStrategy.AGDLP && currentSecGroup.Scope == GroupScope.Local
|| groupPermissionStrategy == PermissionGroupStrategy.AGP && currentSecGroup.Scope == GroupScope.Global)
{
resultToken.addedAclEntries.Add(currentSecGroup.Name);
}
}
resultToken.resultErrorId = 0;
resultToken.resultMessage = "Verzeichnis-, Gruppen- und ACL-Vorschau erfolgreich erstellt";
return resultToken;
}
DefaultLogger.LogEntry(LogLevels.Debug, $"Creating folder: {newDataArea.IAM_Folders[0].technicalName}");
DirectoryInfo newDir = Directory.CreateDirectory(newDataArea.IAM_Folders[0].technicalName);
newDataArea.IAM_Folders[0].UID = DataArea.GetUniqueDataAreaID(newDir.FullName);
for (int i = 0; newSecurityGroups.IAM_SecurityGroups.Count > i; i++)
{
var currentSecGroup = newSecurityGroups.IAM_SecurityGroups[i];
var sid = new SecurityIdentifier(currentSecGroup.UID);
if (groupPermissionStrategy == PermissionGroupStrategy.AGDLP && currentSecGroup.Scope == GroupScope.Local
|| groupPermissionStrategy == PermissionGroupStrategy.AGP && currentSecGroup.Scope == GroupScope.Global)
DataArea.AddDirectorySecurity(newDataArea.IAM_Folders[0].baseFolder, newDataArea.IAM_Folders[0].technicalName, sid, currentSecGroup.rights, AccessControlType.Allow);
}
AddDirectorySecurityACLDenyFolderDeletion(newDataArea.IAM_Folders[0].technicalName);
resultToken.resultErrorId = 0;
resultToken.resultMessage = "Verzeichnis erfolgreich erstellt";
}
return resultToken;
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
private void AddDirectorySecurityACLDenyFolderDeletion(string technicalName)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
// Create a new DirectoryInfo object.
DirectoryInfo dInfo = new DirectoryInfo(technicalName);
//DirectoryInfo dInfoBaseFolder = new DirectoryInfo(baseFolderTechnicalName);
// Get a DirectorySecurity object that represents the
// current security settings.
DirectorySecurity dSecurity = dInfo.GetAccessControl();
// Add the FileSystemAccessRule to the security settings.
var everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
dSecurity.AddAccessRule(new FileSystemAccessRule(everyone,
FileSystemRights.Delete, InheritanceFlags.None, PropagationFlags.None,
AccessControlType.Deny));
// Set the new access settings.
dInfo.SetAccessControl(dSecurity);
LogMethodEnd(MethodBase.GetCurrentMethod());
}
private void createADGroups(ResultToken resultToken)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
Dictionary<FileSystemRights, string> currentGroupDIC = new Dictionary<FileSystemRights, string>();
adGroupDic = new Dictionary<FileSystemRights, string>();
var existingADGroupCount = 0;
do
{
newSecurityGroups.IAM_SecurityGroups.Clear();
newSecurityGroups.GenerateNewSecurityGroups(baseFolder,
newDataArea.IAM_Folders[0].technicalName,
groupPrefix,
groupOUPath,
groupPermissionStrategy,
groupTraverseTag,
groupReadTag,
groupWriteTag,
groupOwnerTag,
groupDLTag,
groupGTag,
groupCustomTags,
templates,
ReadACLPermission,
WriteACLPermission,
OwnerACLPermission,
existingADGroupCount);
/*
if (existingADGroupCount > 0 && !templates.All(t => t.Type == SecurityGroupType.Traverse || Regex.IsMatch(t.NamingTemplate, @"(?<loopTag>{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}})")))
{
var nt = templates.First(t => t.Type == SecurityGroupType.Traverse || Regex.IsMatch(t.NamingTemplate, @"(?<loopTag>{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}})")).NamingTemplate;
DefaultLogger.LogEntry(LogLevels.Debug, $"Naming template: {nt}");
throw new Exception("AD groups already existing and loop tag not found");
}
*/
existingADGroupCount++;
} while (newSecurityGroups.GroupsAllreadyExisting(groupOUPath) && existingADGroupCount < 1000);
if(newSecurityGroups.GroupsAllreadyExisting(groupOUPath) && existingADGroupCount>= 1000)
{
throw new Exception("Unique AD Group could not be generated after 1000 iterations");
}
List<UserPrincipal> users;
List<UserPrincipal> owners = getUserPrincipalBySid(ownerUserSids);
List<UserPrincipal> writers = getUserPrincipalBySid(writerUserSids);
List<UserPrincipal> readers = getUserPrincipalBySid(readerUserSids);
for (int i = 0; newSecurityGroups.IAM_SecurityGroups.Count > i; i++)
{
if (newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper().EndsWith(groupOwnerTag.ToUpper()))
users = owners;
else if (newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper().EndsWith(groupWriteTag.ToUpper()))
users = writers;
else if (newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper().EndsWith(groupReadTag.ToUpper()))
users = readers;
else
users = null;
if (WhatIf)
{
var existingGroup = newSecurityGroups.PreviewADGroup(groupOUPath, newSecurityGroups.IAM_SecurityGroups[i], newDataArea.IAM_Folders[0].technicalName);
newSecurityGroups.IAM_SecurityGroups[i].CreatedNewEntry = existingGroup == null;
}
else
{
newSecurityGroups.CreateADGroup(groupOUPath, newSecurityGroups.IAM_SecurityGroups[i], users);
}
if (resultToken != null)
{
if (newSecurityGroups.IAM_SecurityGroups[i].CreatedNewEntry)
resultToken.createdGroups.Add(newSecurityGroups.IAM_SecurityGroups[i].Name);
else
resultToken.reusedGroups.Add(newSecurityGroups.IAM_SecurityGroups[i].Name);
}
}
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
private List<UserPrincipal> getUserPrincipalBySid(ICollection<string> UserSids)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
List<UserPrincipal> result = new List<UserPrincipal>();
if (UserSids != null)
{
foreach (var sid in UserSids)
{
if (!string.IsNullOrEmpty(sid))
{
UserPrincipal user = getUserPrincipalBySid(sid);
if (user != null && !result.Any(item => item.DistinguishedName == user.DistinguishedName))
{
result.Add(user);
}
}
}
}
return result;
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
private UserPrincipal getUserPrincipalBySid(string sid)
{
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domainName, username, new NetworkCredential("", password).Password);
UserPrincipal user;
user = UserPrincipal.FindByIdentity(ctx, IdentityType.Sid, (sid));
return user;
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
throw;
}
finally
{
LogMethodEnd(MethodBase.GetCurrentMethod());
}
}
private string getTraverseRegex()
{
return groupWildcard
.Replace("{{PREFIX}}", groupPrefix)
.Replace("{{SCOPETAG}}", groupDLTag)
.Replace("{{GROUPTYPEPOSTFIX}}", groupTraverseTag);
}
}
}