feat: ensure missing NTFS permission groups
This commit is contained in:
@@ -311,7 +311,7 @@ namespace C4IT.LIAM
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<ResultToken> CreateDataAreaAsync(
|
||||
public Task<ResultToken> CreateDataAreaAsync(
|
||||
string newFolderPath,
|
||||
string newFolderParent,
|
||||
IDictionary<string, string> customTags,
|
||||
@@ -320,27 +320,157 @@ namespace C4IT.LIAM
|
||||
IEnumerable<string> writerSids
|
||||
)
|
||||
{
|
||||
// 1) Instanziere DataArea_FileSystem und fülle Konfiguration:
|
||||
var engine = CreateFilesystemEngine(
|
||||
newFolderPath,
|
||||
newFolderParent,
|
||||
customTags,
|
||||
ownerSids,
|
||||
readerSids,
|
||||
writerSids);
|
||||
var result = engine.createDataArea();
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
public Task<ResultToken> EnsureMissingPermissionGroupsAsync(
|
||||
string folderPath,
|
||||
IDictionary<string, string> customTags,
|
||||
IEnumerable<string> ownerSids,
|
||||
IEnumerable<string> readerSids,
|
||||
IEnumerable<string> writerSids,
|
||||
bool ensureTraverseGroups = false)
|
||||
{
|
||||
var parentPath = Directory.GetParent(folderPath)?.FullName;
|
||||
var engine = CreateFilesystemEngine(
|
||||
folderPath,
|
||||
parentPath,
|
||||
customTags,
|
||||
ownerSids,
|
||||
readerSids,
|
||||
writerSids);
|
||||
|
||||
return Task.FromResult(engine.ensureDataAreaPermissions(ensureTraverseGroups));
|
||||
}
|
||||
|
||||
private DataArea_FileSystem CreateFilesystemEngine(
|
||||
string folderPath,
|
||||
string parentFolderPath,
|
||||
IDictionary<string, string> customTags,
|
||||
IEnumerable<string> ownerSids,
|
||||
IEnumerable<string> readerSids,
|
||||
IEnumerable<string> writerSids)
|
||||
{
|
||||
var mergedCustomTags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var tag in CustomTags)
|
||||
mergedCustomTags[tag.Key] = tag.Value;
|
||||
if (customTags != null)
|
||||
{
|
||||
foreach (var tag in customTags)
|
||||
mergedCustomTags[tag.Key] = tag.Value;
|
||||
}
|
||||
|
||||
var engine = new DataArea_FileSystem
|
||||
{
|
||||
ConfigID = "manual",
|
||||
domainName = this.Domain,
|
||||
username = this.Credential.Identification,
|
||||
password = new NetworkCredential("", this.Credential.Secret).SecurePassword,
|
||||
baseFolder = this.RootPath,
|
||||
newFolderPath = newFolderPath,
|
||||
newFolderParent = newFolderParent,
|
||||
groupPrefix = CustomTags["Filesystem_GroupPrefixTag"],
|
||||
newFolderPath = folderPath,
|
||||
newFolderParent = parentFolderPath,
|
||||
groupPrefix = GetRequiredCustomTag("Filesystem_GroupPrefixTag"),
|
||||
groupOUPath = this.GroupPath,
|
||||
groupPermissionStrategy = (C4IT_IAM_GET.PermissionGroupStrategy)this.GroupStrategy,
|
||||
groupCustomTags = customTags,
|
||||
ownerUserSids = ownerSids?.ToList(),
|
||||
readerUserSids = readerSids?.ToList(),
|
||||
writerUserSids = writerSids?.ToList(),
|
||||
// Templates aus NamingConventions übernehmen…
|
||||
groupCustomTags = mergedCustomTags,
|
||||
ownerUserSids = ownerSids?.Where(i => !string.IsNullOrWhiteSpace(i)).Distinct(StringComparer.OrdinalIgnoreCase).ToList() ?? new List<string>(),
|
||||
readerUserSids = readerSids?.Where(i => !string.IsNullOrWhiteSpace(i)).Distinct(StringComparer.OrdinalIgnoreCase).ToList() ?? new List<string>(),
|
||||
writerUserSids = writerSids?.Where(i => !string.IsNullOrWhiteSpace(i)).Distinct(StringComparer.OrdinalIgnoreCase).ToList() ?? new List<string>(),
|
||||
groupOwnerTag = GetRequiredCustomTag("Filesystem_GroupOwnerTag"),
|
||||
groupWriteTag = GetRequiredCustomTag("Filesystem_GroupWriteTag"),
|
||||
groupReadTag = GetRequiredCustomTag("Filesystem_GroupReadTag"),
|
||||
groupTraverseTag = GetRequiredCustomTag("Filesystem_GroupTraverseTag"),
|
||||
groupDLTag = GetRequiredCustomTag("Filesystem_GroupDomainLocalTag"),
|
||||
groupGTag = GetRequiredCustomTag("Filesystem_GroupGlobalTag")
|
||||
};
|
||||
// 2) Engine starten
|
||||
var result = engine.createDataArea();
|
||||
return result;
|
||||
|
||||
foreach (var template in BuildSecurityGroupTemplates())
|
||||
engine.templates.Add(template);
|
||||
|
||||
return engine;
|
||||
}
|
||||
|
||||
private IEnumerable<IAM_SecurityGroupTemplate> BuildSecurityGroupTemplates()
|
||||
{
|
||||
var templates = new List<IAM_SecurityGroupTemplate>();
|
||||
foreach (var namingConvention in NamingConventions ?? Enumerable.Empty<cLiamNamingConvention>())
|
||||
{
|
||||
if (!TryMapSecurityGroupType(namingConvention.AccessRole, out var securityGroupType))
|
||||
continue;
|
||||
|
||||
if (!TryMapGroupScope(namingConvention.Scope, securityGroupType, out var groupScope))
|
||||
continue;
|
||||
|
||||
templates.Add(new IAM_SecurityGroupTemplate(
|
||||
namingConvention.NamingTemplate,
|
||||
namingConvention.DescriptionTemplate,
|
||||
namingConvention.Wildcard,
|
||||
securityGroupType,
|
||||
groupScope));
|
||||
}
|
||||
|
||||
return templates;
|
||||
}
|
||||
|
||||
private bool TryMapSecurityGroupType(eLiamAccessRoles accessRole, out SecurityGroupType securityGroupType)
|
||||
{
|
||||
securityGroupType = SecurityGroupType.Read;
|
||||
switch (accessRole)
|
||||
{
|
||||
case eLiamAccessRoles.Owner:
|
||||
securityGroupType = SecurityGroupType.Owner;
|
||||
return true;
|
||||
case eLiamAccessRoles.Write:
|
||||
securityGroupType = SecurityGroupType.Write;
|
||||
return true;
|
||||
case eLiamAccessRoles.Read:
|
||||
securityGroupType = SecurityGroupType.Read;
|
||||
return true;
|
||||
case eLiamAccessRoles.Traverse:
|
||||
securityGroupType = SecurityGroupType.Traverse;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryMapGroupScope(eLiamAccessRoleScopes scope, SecurityGroupType type, out GroupScope groupScope)
|
||||
{
|
||||
groupScope = GroupScope.Global;
|
||||
switch (scope)
|
||||
{
|
||||
case eLiamAccessRoleScopes.Global:
|
||||
groupScope = GroupScope.Global;
|
||||
return true;
|
||||
case eLiamAccessRoleScopes.DomainLocal:
|
||||
groupScope = GroupScope.Local;
|
||||
return true;
|
||||
case eLiamAccessRoleScopes.Unknown:
|
||||
if (type == SecurityGroupType.Traverse)
|
||||
{
|
||||
groupScope = this.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP ? GroupScope.Local : GroupScope.Global;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetRequiredCustomTag(string key)
|
||||
{
|
||||
if (!CustomTags.TryGetValue(key, out var value))
|
||||
throw new InvalidOperationException($"Missing NTFS custom tag '{key}'.");
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -11,6 +11,12 @@ namespace C4IT_IAM_Engine
|
||||
public string resultMessage;
|
||||
public int resultErrorId;
|
||||
public string resultFunction;
|
||||
public List<string> createdGroups = new List<string>();
|
||||
public List<string> reusedGroups = new List<string>();
|
||||
public List<string> addedAclEntries = new List<string>();
|
||||
public List<string> skippedAclEntries = new List<string>();
|
||||
public List<string> ensuredTraverseGroups = new List<string>();
|
||||
public List<string> warnings = new List<string>();
|
||||
public ResultToken(string function)
|
||||
{
|
||||
this.resultFunction = function;
|
||||
|
||||
@@ -134,19 +134,19 @@ namespace C4IT_IAM_Engine
|
||||
try
|
||||
{
|
||||
|
||||
var relativePathRaw = DataArea.GetRelativePath(newFolderPath, baseFolder).Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
relativePathRaw = relativePathRaw.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
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;
|
||||
var folderName = sanitizedSegments.Length > 0
|
||||
? sanitizedSegments[sanitizedSegments.Length - 1]
|
||||
: Helper.SanitizePathSegment(Path.GetFileName(newFolderPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)));
|
||||
|
||||
foreach (var template in templates)
|
||||
{
|
||||
var GroupTypeTag = "";
|
||||
switch (template.Type)
|
||||
var relativePathRaw = DataArea.GetRelativePath(newFolderPath, baseFolder).Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
relativePathRaw = relativePathRaw.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
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;
|
||||
var folderName = sanitizedSegments.Length > 0
|
||||
? sanitizedSegments[sanitizedSegments.Length - 1]
|
||||
: Helper.SanitizePathSegment(Path.GetFileName(newFolderPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)));
|
||||
|
||||
foreach (var template in templates)
|
||||
{
|
||||
var GroupTypeTag = "";
|
||||
switch (template.Type)
|
||||
{
|
||||
case SecurityGroupType.Owner:
|
||||
GroupTypeTag = groupOwnerTag;
|
||||
@@ -181,20 +181,20 @@ namespace C4IT_IAM_Engine
|
||||
tags.Add("GROUPTYPEPOSTFIX", GroupTypeTag);
|
||||
tags.Add("SCOPETAG", GroupScopeTag);
|
||||
|
||||
template.NamingTemplate = Helper.ApplyTemplatePlaceholders(template.NamingTemplate, template.Type != SecurityGroupType.Traverse, relativePath, sanitizedSegments, folderName)
|
||||
.ReplaceTags(customTags).ReplaceTags(tags)
|
||||
.ToUpper();
|
||||
|
||||
template.DescriptionTemplate = Helper.ApplyTemplatePlaceholders(template.DescriptionTemplate, template.Type != SecurityGroupType.Traverse, relativePath, sanitizedSegments, folderName)
|
||||
.ReplaceTags(customTags).ReplaceTags(tags)
|
||||
.ToUpper();
|
||||
|
||||
|
||||
template.WildcardTemplate = Helper.ApplyTemplatePlaceholders(template.WildcardTemplate, template.Type != SecurityGroupType.Traverse, relativePath, sanitizedSegments, folderName)
|
||||
.ReplaceTags(customTags).ReplaceTags(tags)
|
||||
.ToUpper();
|
||||
|
||||
}
|
||||
template.NamingTemplate = Helper.ApplyTemplatePlaceholders(template.NamingTemplate, template.Type != SecurityGroupType.Traverse, relativePath, sanitizedSegments, folderName)
|
||||
.ReplaceTags(customTags).ReplaceTags(tags)
|
||||
.ToUpper();
|
||||
|
||||
template.DescriptionTemplate = Helper.ApplyTemplatePlaceholders(template.DescriptionTemplate, template.Type != SecurityGroupType.Traverse, relativePath, sanitizedSegments, folderName)
|
||||
.ReplaceTags(customTags).ReplaceTags(tags)
|
||||
.ToUpper();
|
||||
|
||||
|
||||
template.WildcardTemplate = Helper.ApplyTemplatePlaceholders(template.WildcardTemplate, template.Type != SecurityGroupType.Traverse, relativePath, sanitizedSegments, folderName)
|
||||
.ReplaceTags(customTags).ReplaceTags(tags)
|
||||
.ToUpper();
|
||||
|
||||
}
|
||||
|
||||
IAM_SecurityGroupTemplate ownerGlobal = templates.First(t => t.Scope.Equals(GroupScope.Global) && t.Type.Equals(SecurityGroupType.Owner));
|
||||
IAM_SecurityGroup osecGroup = new IAM_SecurityGroup()
|
||||
@@ -359,9 +359,111 @@ namespace C4IT_IAM_Engine
|
||||
LogMethodEnd(MethodBase.GetCurrentMethod());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public DirectoryEntry CreateADGroup(string ouPath, IAM_SecurityGroup secGroup, List<UserPrincipal> users)
|
||||
}
|
||||
|
||||
private DirectoryEntry FindGroupEntry(string groupName)
|
||||
{
|
||||
DirectoryEntry entry = new DirectoryEntry
|
||||
{
|
||||
Path = "LDAP://" + domainName,
|
||||
Username = username,
|
||||
Password = new NetworkCredential("", password).Password,
|
||||
AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing
|
||||
};
|
||||
|
||||
DirectorySearcher search = new DirectorySearcher(entry)
|
||||
{
|
||||
Filter = "(&(objectClass=group)(sAMAccountName=" + groupName.ToUpper() + "))"
|
||||
};
|
||||
search.PageSize = 100000;
|
||||
|
||||
var result = search.FindOne();
|
||||
if (result == null)
|
||||
{
|
||||
entry.Dispose();
|
||||
search.Dispose();
|
||||
return null;
|
||||
}
|
||||
|
||||
var groupEntry = result.GetDirectoryEntry();
|
||||
search.Dispose();
|
||||
entry.Dispose();
|
||||
return groupEntry;
|
||||
}
|
||||
|
||||
private static bool HasMember(PropertyValueCollection members, string distinguishedName)
|
||||
{
|
||||
foreach (var member in members)
|
||||
{
|
||||
if (string.Equals(member?.ToString(), distinguishedName, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void AddMissingMembers(DirectoryEntry group, IAM_SecurityGroup secGroup, List<UserPrincipal> users)
|
||||
{
|
||||
if (group == null)
|
||||
return;
|
||||
|
||||
var changed = false;
|
||||
if (users != null && secGroup.Scope == GroupScope.Global)
|
||||
{
|
||||
foreach (var user in users.Where(u => u != null && !string.IsNullOrWhiteSpace(u.DistinguishedName)))
|
||||
{
|
||||
if (HasMember(group.Properties["member"], user.DistinguishedName))
|
||||
continue;
|
||||
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Adding missing member: {user.DistinguishedName}");
|
||||
group.Properties["member"].Add(user.DistinguishedName);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (secGroup.Scope == GroupScope.Local)
|
||||
{
|
||||
foreach (var innerGroup in secGroup.memberGroups.Where(g => g != null && !string.IsNullOrWhiteSpace(g.technicalName)))
|
||||
{
|
||||
if (HasMember(group.Properties["member"], innerGroup.technicalName))
|
||||
continue;
|
||||
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Adding missing nested group: {innerGroup.technicalName}");
|
||||
group.Properties["member"].Add(innerGroup.technicalName);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
group.CommitChanges();
|
||||
}
|
||||
|
||||
public DirectoryEntry EnsureADGroup(string ouPath, IAM_SecurityGroup secGroup, List<UserPrincipal> users)
|
||||
{
|
||||
LogMethodBegin(MethodBase.GetCurrentMethod());
|
||||
try
|
||||
{
|
||||
var existingGroup = FindGroupEntry(secGroup.Name);
|
||||
if (existingGroup == null)
|
||||
return CreateADGroup(ouPath, secGroup, users);
|
||||
|
||||
AddMissingMembers(existingGroup, secGroup, users);
|
||||
var objectid = getSID(existingGroup);
|
||||
secGroup.UID = objectid;
|
||||
return existingGroup;
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
cLogManager.DefaultLogger.LogException(E);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
LogMethodEnd(MethodBase.GetCurrentMethod());
|
||||
}
|
||||
}
|
||||
|
||||
public DirectoryEntry CreateADGroup(string ouPath, IAM_SecurityGroup secGroup, List<UserPrincipal> users)
|
||||
{
|
||||
LogMethodBegin(MethodBase.GetCurrentMethod());
|
||||
try
|
||||
@@ -406,9 +508,13 @@ namespace C4IT_IAM_Engine
|
||||
}
|
||||
else
|
||||
{
|
||||
DirectoryEntry e = new DirectoryEntry("LDAP://" + domainName + "/" + "CN =" + secGroup.Name.ToUpper() + "," + ouPath, username, new NetworkCredential("", password).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
|
||||
DirectoryEntry e = FindGroupEntry(secGroup.Name);
|
||||
if (e == null)
|
||||
return null;
|
||||
var objectid = getSID(e);
|
||||
secGroup.UID = objectid;
|
||||
AddMissingMembers(e, secGroup, users);
|
||||
return e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user