feat: ensure missing NTFS permission groups

This commit is contained in:
Meik
2026-03-10 08:51:59 +01:00
parent 58d7529329
commit 812deeaa74
6 changed files with 1049 additions and 375 deletions

View File

@@ -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;
}

View File

@@ -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());

View File

@@ -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;

View File

@@ -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;
}