Allow regex reuse for NTFS ensure groups

This commit is contained in:
Meik
2026-03-13 14:36:23 +01:00
parent d28cfe008c
commit 32021dcfd8
3 changed files with 123 additions and 8 deletions

View File

@@ -563,7 +563,8 @@ namespace C4IT.LIAM
groupReadTag = GetRequiredCustomTag("Filesystem_GroupReadTag"),
groupTraverseTag = GetRequiredCustomTag("Filesystem_GroupTraverseTag"),
groupDLTag = requiresDomainLocalTag ? GetRequiredCustomTag("Filesystem_GroupDomainLocalTag") : string.Empty,
groupGTag = GetRequiredCustomTag("Filesystem_GroupGlobalTag")
groupGTag = GetRequiredCustomTag("Filesystem_GroupGlobalTag"),
allowExistingGroupWildcardMatch = IsAdditionalConfigurationEnabled("EnsureNtfsPermissionGroupsAllowRegexMatch")
};
foreach (var template in BuildSecurityGroupTemplates())
@@ -572,6 +573,19 @@ namespace C4IT.LIAM
return engine;
}
private bool IsAdditionalConfigurationEnabled(string key)
{
if (AdditionalConfiguration == null || string.IsNullOrWhiteSpace(key))
return false;
if (!AdditionalConfiguration.TryGetValue(key, out var rawValue) || string.IsNullOrWhiteSpace(rawValue))
return false;
return rawValue.Equals("true", StringComparison.OrdinalIgnoreCase)
|| rawValue.Equals("1", StringComparison.OrdinalIgnoreCase)
|| rawValue.Equals("yes", StringComparison.OrdinalIgnoreCase);
}
private IEnumerable<IAM_SecurityGroupTemplate> BuildSecurityGroupTemplates()
{
var templates = new List<IAM_SecurityGroupTemplate>();

View File

@@ -51,6 +51,7 @@ namespace C4IT_IAM_SET
public ICollection<string> ownerUserSids;
public ICollection<string> readerUserSids;
public ICollection<string> writerUserSids;
public bool allowExistingGroupWildcardMatch;
public int ReadACLPermission = 0x200A9;
public int WriteACLPermission = 0x301BF;
@@ -144,6 +145,7 @@ namespace C4IT_IAM_SET
newSecurityGroups.username = username;
newSecurityGroups.domainName = domainName;
newSecurityGroups.password = password;
newSecurityGroups.AllowExistingGroupWildcardMatch = allowExistingGroupWildcardMatch;
try
{
// ImpersonationHelper.Impersonate(domainName, username, new NetworkCredential("", password).Password, delegate
@@ -274,7 +276,8 @@ namespace C4IT_IAM_SET
{
username = username,
domainName = domainName,
password = password
password = password,
AllowExistingGroupWildcardMatch = allowExistingGroupWildcardMatch
};
}
@@ -909,9 +912,8 @@ namespace C4IT_IAM_SET
else
users = null;
var groupAlreadyExists = newSecurityGroups.GroupAllreadyExisting(newSecurityGroups.IAM_SecurityGroups[i].Name.ToUpper());
newSecurityGroups.EnsureADGroup(groupOUPath, newSecurityGroups.IAM_SecurityGroups[i], users);
if (groupAlreadyExists)
if (newSecurityGroups.IAM_SecurityGroups[i].ReusedExistingEntry)
resultToken.reusedGroups.Add(newSecurityGroups.IAM_SecurityGroups[i].Name);
else
resultToken.createdGroups.Add(newSecurityGroups.IAM_SecurityGroups[i].Name);

View File

@@ -23,6 +23,7 @@ namespace C4IT_IAM_Engine
public string domainName;
public string username;
public SecureString password;
public bool AllowExistingGroupWildcardMatch;
public List<IAM_SecurityGroup> IAM_SecurityGroups;
public string rootUID;
@@ -208,6 +209,7 @@ namespace C4IT_IAM_Engine
{
Name = ownerGlobal.NamingTemplate,
description = ownerGlobal.DescriptionTemplate,
WildcardPattern = ownerGlobal.WildcardTemplate,
technicalName = "CN=" + ownerGlobal.NamingTemplate + "," + ouPath,
targetTyp = (int)IAM_TargetType.FileSystem,
@@ -221,6 +223,7 @@ namespace C4IT_IAM_Engine
{
Name = writeGlobal.NamingTemplate,
description = writeGlobal.DescriptionTemplate,
WildcardPattern = writeGlobal.WildcardTemplate,
technicalName = "CN=" + writeGlobal.NamingTemplate + "," + ouPath,
targetTyp = (int)IAM_TargetType.FileSystem,
@@ -234,6 +237,7 @@ namespace C4IT_IAM_Engine
{
Name = readGlobal.NamingTemplate,
description = readGlobal.DescriptionTemplate,
WildcardPattern = readGlobal.WildcardTemplate,
technicalName = "CN=" + readGlobal.NamingTemplate + "," + ouPath,
targetTyp = (int)IAM_TargetType.FileSystem,
@@ -251,6 +255,7 @@ namespace C4IT_IAM_Engine
{
Name = ownerDL.NamingTemplate,
description = ownerDL.DescriptionTemplate,
WildcardPattern = ownerDL.WildcardTemplate,
technicalName = "CN=" + ownerDL.NamingTemplate + "," + ouPath,
targetTyp = (int)IAM_TargetType.FileSystem,
@@ -265,6 +270,7 @@ namespace C4IT_IAM_Engine
{
Name = writeDL.NamingTemplate,
description = writeDL.DescriptionTemplate,
WildcardPattern = writeDL.WildcardTemplate,
technicalName = "CN=" + writeDL.NamingTemplate + "," + ouPath,
targetTyp = (int)IAM_TargetType.FileSystem,
@@ -279,6 +285,7 @@ namespace C4IT_IAM_Engine
{
Name = readDL.NamingTemplate,
description = readDL.DescriptionTemplate,
WildcardPattern = readDL.WildcardTemplate,
technicalName = "CN=" + readDL.NamingTemplate + "," + ouPath,
targetTyp = (int)IAM_TargetType.FileSystem,
@@ -293,6 +300,7 @@ namespace C4IT_IAM_Engine
secGroup.description = secGroup.description.ReplaceLoopTag(0);
secGroup.Name = secGroup.Name.ReplaceLoopTag(loop);
secGroup.technicalName = secGroup.technicalName.ReplaceLoopTag(loop);
secGroup.WildcardPattern = secGroup.WildcardPattern.ReplaceLoopTag(loop);
DefaultLogger.LogEntry(LogLevels.Debug, $"Security group generated: {secGroup.technicalName}");
}
}
@@ -398,6 +406,92 @@ namespace C4IT_IAM_Engine
return groupEntry;
}
private DirectoryEntry FindGroupEntryByWildcard(string ouPath, string wildcardPattern)
{
if (string.IsNullOrWhiteSpace(wildcardPattern))
return null;
Regex wildcardRegex;
try
{
wildcardRegex = new Regex(wildcardPattern, RegexOptions.IgnoreCase);
}
catch (Exception E)
{
cLogManager.DefaultLogger.LogException(E);
return null;
}
var basePath = "LDAP://" + domainName;
if (!string.IsNullOrWhiteSpace(ouPath))
basePath += "/" + ouPath;
DirectoryEntry entry = new DirectoryEntry
{
Path = basePath,
Username = username,
Password = new NetworkCredential("", password).Password,
AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing
};
DirectorySearcher search = new DirectorySearcher(entry)
{
Filter = "(objectClass=group)"
};
search.PageSize = 100000;
search.PropertiesToLoad.Add("sAMAccountName");
search.PropertiesToLoad.Add("distinguishedName");
string matchedName = null;
string matchedDistinguishedName = null;
var matchCount = 0;
foreach (SearchResult result in search.FindAll())
{
if (!result.Properties.Contains("sAMAccountName") || result.Properties["sAMAccountName"].Count == 0)
continue;
var samAccountName = result.Properties["sAMAccountName"][0]?.ToString();
if (string.IsNullOrWhiteSpace(samAccountName) || !wildcardRegex.IsMatch(samAccountName))
continue;
matchCount++;
if (matchCount > 1)
{
DefaultLogger.LogEntry(LogLevels.Warning, $"Multiple AD groups matched wildcard '{wildcardPattern}' in '{basePath}'. Regex-based reuse is skipped.");
search.Dispose();
entry.Dispose();
return null;
}
matchedName = samAccountName;
matchedDistinguishedName = result.Properties.Contains("distinguishedName") && result.Properties["distinguishedName"].Count > 0
? result.Properties["distinguishedName"][0]?.ToString()
: null;
}
search.Dispose();
entry.Dispose();
if (string.IsNullOrWhiteSpace(matchedDistinguishedName))
return null;
DefaultLogger.LogEntry(LogLevels.Debug, $"Reusing existing AD group '{matchedName}' via wildcard '{wildcardPattern}'.");
return new DirectoryEntry("LDAP://" + domainName + "/" + matchedDistinguishedName, username, new NetworkCredential("", password).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
}
private void ApplyExistingGroup(IAM_SecurityGroup secGroup, DirectoryEntry existingGroup)
{
secGroup.ReusedExistingEntry = true;
secGroup.UID = getSID(existingGroup);
if (existingGroup.Properties.Contains("sAMAccountName") && existingGroup.Properties["sAMAccountName"].Count > 0)
secGroup.Name = existingGroup.Properties["sAMAccountName"][0]?.ToString();
if (existingGroup.Properties.Contains("distinguishedName") && existingGroup.Properties["distinguishedName"].Count > 0)
secGroup.technicalName = existingGroup.Properties["distinguishedName"][0]?.ToString();
}
private static bool HasMember(PropertyValueCollection members, string distinguishedName)
{
foreach (var member in members)
@@ -450,13 +544,16 @@ namespace C4IT_IAM_Engine
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
secGroup.ReusedExistingEntry = false;
var existingGroup = FindGroupEntry(secGroup.Name);
if (existingGroup == null && AllowExistingGroupWildcardMatch)
existingGroup = FindGroupEntryByWildcard(ouPath, secGroup.WildcardPattern);
if (existingGroup == null)
return CreateADGroup(ouPath, secGroup, users);
AddMissingMembers(existingGroup, secGroup, users);
var objectid = getSID(existingGroup);
secGroup.UID = objectid;
ApplyExistingGroup(secGroup, existingGroup);
return existingGroup;
}
catch (Exception E)
@@ -475,6 +572,7 @@ namespace C4IT_IAM_Engine
LogMethodBegin(MethodBase.GetCurrentMethod());
try
{
secGroup.ReusedExistingEntry = false;
if (!GroupAllreadyExisting(secGroup.Name.ToUpper()))
{
@@ -518,9 +616,8 @@ namespace C4IT_IAM_Engine
DirectoryEntry e = FindGroupEntry(secGroup.Name);
if (e == null)
return null;
var objectid = getSID(e);
secGroup.UID = objectid;
AddMissingMembers(e, secGroup, users);
ApplyExistingGroup(secGroup, e);
return e;
}
return null;
@@ -588,6 +685,8 @@ namespace C4IT_IAM_Engine
public string UID;
public string Parent = "";
public string description;
public string WildcardPattern;
public bool ReusedExistingEntry;
public List<IAM_SecurityGroup> memberGroups;
public string Name;
public string technicalName;