feat: ensure missing NTFS permission groups
This commit is contained in:
@@ -225,6 +225,134 @@ namespace C4IT_IAM_SET
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 = "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());
|
||||
@@ -349,30 +477,33 @@ namespace C4IT_IAM_SET
|
||||
continue;
|
||||
}
|
||||
|
||||
GroupPrincipal parentTraverseGroup = null;
|
||||
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)));
|
||||
|
||||
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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
@@ -397,12 +528,29 @@ namespace C4IT_IAM_SET
|
||||
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.");
|
||||
@@ -412,24 +560,22 @@ namespace C4IT_IAM_SET
|
||||
continue;
|
||||
}
|
||||
|
||||
IAM_SecurityGroup newTraverseGroup = null;
|
||||
var traverseNameTemplate = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.NamingTemplate, true, relativePath, sanitizedSegments, folderName);
|
||||
var traverseDescriptionTemplate = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.DescriptionTemplate, true, relativePath, sanitizedSegments, folderName);
|
||||
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++;
|
||||
}
|
||||
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}");
|
||||
@@ -452,6 +598,8 @@ namespace C4IT_IAM_SET
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -474,6 +622,8 @@ namespace C4IT_IAM_SET
|
||||
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)
|
||||
{
|
||||
@@ -488,6 +638,37 @@ namespace C4IT_IAM_SET
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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)
|
||||
@@ -566,6 +747,9 @@ namespace C4IT_IAM_SET
|
||||
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)
|
||||
@@ -620,6 +804,136 @@ namespace C4IT_IAM_SET
|
||||
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 (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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
var groupAlreadyExists = newSecurityGroups.GroupAllreadyExisting(newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper());
|
||||
newSecurityGroups.EnsureADGroup(groupOUPath, newSecurityGroups.IAM_SecurityGroups[i], users);
|
||||
if (groupAlreadyExists)
|
||||
resultToken.reusedGroups.Add(newSecurityGroups.IAM_SecurityGroups[i].Name);
|
||||
else
|
||||
resultToken.createdGroups.Add(newSecurityGroups.IAM_SecurityGroups[i].Name);
|
||||
}
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
cLogManager.DefaultLogger.LogException(E);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
LogMethodEnd(MethodBase.GetCurrentMethod());
|
||||
}
|
||||
}
|
||||
|
||||
private ResultToken createFolder()
|
||||
{
|
||||
LogMethodBegin(MethodBase.GetCurrentMethod());
|
||||
|
||||
Reference in New Issue
Block a user