Compare commits

...

2 Commits

Author SHA1 Message Date
Meik
32021dcfd8 Allow regex reuse for NTFS ensure groups 2026-03-13 14:36:23 +01:00
Meik
d28cfe008c Classify NTFS and DFS data areas 2026-03-13 13:27:00 +01:00
5 changed files with 394 additions and 80 deletions

View File

@@ -32,6 +32,7 @@ namespace C4IT.LIAM
Unknown = 0, Unknown = 0,
NtfsShare = 101, NtfsShare = 101,
NtfsFolder = 102, NtfsFolder = 102,
DfsNamespaceRoot = 103,
MsTeamsTeam = 401, MsTeamsTeam = 401,
MsTeamsChannel = 402, MsTeamsChannel = 402,
MsTeamsFolder = 403, MsTeamsFolder = 403,

View File

@@ -12,6 +12,7 @@ using System.Threading.Tasks;
using C4IT.Logging; using C4IT.Logging;
using C4IT.Matrix42.ServerInfo; using C4IT.Matrix42.ServerInfo;
using C4IT_IAM;
using C4IT_IAM_Engine; using C4IT_IAM_Engine;
using C4IT_IAM_SET; using C4IT_IAM_SET;
using LiamNtfs; using LiamNtfs;
@@ -30,9 +31,30 @@ namespace C4IT.LIAM
public class cLiamProviderNtfs : cLiamProviderBase public class cLiamProviderNtfs : cLiamProviderBase
{ {
private enum eNtfsPathKind
{
Unknown = 0,
ServerRoot = 1,
ClassicShare = 2,
DfsNamespaceRoot = 3,
DfsLink = 4,
Folder = 5
}
private sealed class cNtfsPathClassification
{
public string NormalizedPath { get; set; } = string.Empty;
public eNtfsPathKind Kind { get; set; } = eNtfsPathKind.Unknown;
public string BoundaryPath { get; set; } = string.Empty;
public string ParentBoundaryPath { get; set; } = string.Empty;
public string DisplayName { get; set; } = string.Empty;
public int Level { get; set; } = -1;
}
public static Guid nftsModuleId = new Guid("77e213a1-6517-ea11-4881-000c2980fd94"); public static Guid nftsModuleId = new Guid("77e213a1-6517-ea11-4881-000c2980fd94");
public readonly cNtfsBase ntfsBase = new cNtfsBase(); public readonly cNtfsBase ntfsBase = new cNtfsBase();
public readonly cActiveDirectoryBase activeDirectoryBase = new cActiveDirectoryBase(); public readonly cActiveDirectoryBase activeDirectoryBase = new cActiveDirectoryBase();
private readonly Dictionary<string, HashSet<string>> publishedShareCache = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase);
//public readonly bool WithoutPrivateFolders = true; //public readonly bool WithoutPrivateFolders = true;
@@ -145,40 +167,15 @@ namespace C4IT.LIAM
return null; return null;
var DataAreas = new List<cLiamDataAreaBase>(); var DataAreas = new List<cLiamDataAreaBase>();
var rootClassification = ClassifyPath(this.RootPath);
var rootpathSplit = this.RootPath.Split(new string[] { "\\" }, StringSplitOptions.RemoveEmptyEntries); var rootDataArea = await BuildDataAreaAsync(rootClassification);
cLiamNtfsShare share = null; if (rootDataArea == null)
cLiamNtfsFolder NtfsRootFolder = null;
switch (rootpathSplit.Length)
{
case 0:
case 1:
return null; return null;
case 2:
{ DataAreas.Add(rootDataArea);
share = new cLiamNtfsShare(this, new cNtfsResultShare()
{ if (Depth == 0)
DisplayName = rootpathSplit.Last(), return DataAreas;
Path = RootPath,
Level = 0
});
await share.ResolvePermissionGroupsAsync(share.TechnicalName);
DataAreas.Add(share);
break;
}
default:
{
NtfsRootFolder = new cLiamNtfsFolder(this, null, null, new cNtfsResultFolder()
{
DisplayName = rootpathSplit.Last(),
Path = RootPath,
Level = 0
});
await NtfsRootFolder.ResolvePermissionGroupsAsync(NtfsRootFolder.TechnicalName);
DataAreas.Add(NtfsRootFolder);
break;
}
}
var DAL = await ntfsBase.RequestFoldersListAsync(this.RootPath, Depth); var DAL = await ntfsBase.RequestFoldersListAsync(this.RootPath, Depth);
if (DAL == null) if (DAL == null)
@@ -189,10 +186,10 @@ namespace C4IT.LIAM
if (!string.IsNullOrEmpty(this.DataAreaRegEx) && !Regex.Match(Entry.Value.DisplayName, this.DataAreaRegEx).Success) if (!string.IsNullOrEmpty(this.DataAreaRegEx) && !Regex.Match(Entry.Value.DisplayName, this.DataAreaRegEx).Success)
continue; continue;
var classification = ClassifyPath(Entry.Value.Path);
var Folder = new cLiamNtfsFolder(this, share, NtfsRootFolder, (cNtfsResultFolder)Entry.Value); var dataArea = await BuildDataAreaAsync(classification, Entry.Value as cNtfsResultFolder);
await Folder.ResolvePermissionGroupsAsync(Folder.TechnicalName); if (dataArea != null)
DataAreas.Add(Folder); DataAreas.Add(dataArea);
} }
return DataAreas; return DataAreas;
} }
@@ -209,7 +206,6 @@ namespace C4IT.LIAM
} }
public override async Task<cLiamDataAreaBase> LoadDataArea(string UID) public override async Task<cLiamDataAreaBase> LoadDataArea(string UID)
{ {
//TODO implement LoadDataArea
var CM = MethodBase.GetCurrentMethod(); var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM); LogMethodBegin(CM);
try try
@@ -222,36 +218,8 @@ namespace C4IT.LIAM
} }
if (!await LogonAsync()) if (!await LogonAsync())
return null; return null;
var splt = UID.Split(System.IO.Path.DirectorySeparatorChar); var classification = ClassifyPath(UID);
var name = Path.GetDirectoryName(UID); return await BuildDataAreaAsync(classification);
switch (splt.Length)
{
case 0:
case 1:
return null;
case 2:
{
var share = new cLiamNtfsShare(this, new cNtfsResultShare()
{
DisplayName = name,
Path = UID,
Level = getDepth(UID)
});
await share.ResolvePermissionGroupsAsync(share.TechnicalName);
return share;
}
default:
{
var folder = new cLiamNtfsFolder(this, null, null, new cNtfsResultFolder()
{
DisplayName = name,
Path = UID,
Level = getDepth(UID)
});
await folder.ResolvePermissionGroupsAsync(folder.TechnicalName);
return folder;
}
}
} }
catch (Exception E) catch (Exception E)
{ {
@@ -265,6 +233,208 @@ namespace C4IT.LIAM
} }
private async Task<cLiamDataAreaBase> BuildDataAreaAsync(cNtfsPathClassification classification, cNtfsResultFolder folderResult = null)
{
if (classification == null)
return null;
switch (classification.Kind)
{
case eNtfsPathKind.ClassicShare:
case eNtfsPathKind.DfsLink:
{
var share = new cLiamNtfsShare(this, new cNtfsResultShare()
{
DisplayName = classification.DisplayName,
Path = classification.NormalizedPath,
Level = classification.Level
}, classification.ParentBoundaryPath);
await share.ResolvePermissionGroupsAsync(share.TechnicalName);
return share;
}
case eNtfsPathKind.DfsNamespaceRoot:
{
var namespaceRoot = new cLiamNtfsDfsNamespaceRoot(this, new cNtfsResultShare()
{
DisplayName = classification.DisplayName,
Path = classification.NormalizedPath,
Level = classification.Level
});
await namespaceRoot.ResolvePermissionGroupsAsync(namespaceRoot.TechnicalName);
return namespaceRoot;
}
case eNtfsPathKind.Folder:
{
var folderData = folderResult ?? new cNtfsResultFolder()
{
DisplayName = classification.DisplayName,
Path = classification.NormalizedPath,
Level = classification.Level,
CreatedDate = Directory.Exists(classification.NormalizedPath)
? new DirectoryInfo(classification.NormalizedPath).CreationTimeUtc.ToString("s")
: DateTime.MinValue.ToString("s")
};
var folder = new cLiamNtfsFolder(this, null, null, folderData, classification.ParentBoundaryPath);
await folder.ResolvePermissionGroupsAsync(folder.TechnicalName);
return folder;
}
default:
return null;
}
}
private cNtfsPathClassification ClassifyPath(string path)
{
var normalizedPath = NormalizeUncPath(path);
var segments = GetUncSegments(normalizedPath);
var classification = new cNtfsPathClassification()
{
NormalizedPath = normalizedPath,
DisplayName = GetDisplayName(normalizedPath),
Level = getDepth(normalizedPath)
};
if (segments.Length == 1)
{
classification.Kind = eNtfsPathKind.ServerRoot;
return classification;
}
if (segments.Length < 2)
return classification;
var serverName = segments[0];
var publishedShares = GetPublishedShareNames(serverName);
var firstBoundaryPath = BuildUncPath(segments, 2);
if (segments.Length == 2)
{
if (publishedShares.Contains(segments[1]))
{
classification.Kind = eNtfsPathKind.ClassicShare;
classification.BoundaryPath = normalizedPath;
return classification;
}
if (Directory.Exists(normalizedPath))
{
classification.Kind = eNtfsPathKind.DfsNamespaceRoot;
classification.BoundaryPath = normalizedPath;
}
return classification;
}
if (publishedShares.Contains(segments[1]))
{
classification.Kind = eNtfsPathKind.Folder;
classification.BoundaryPath = firstBoundaryPath;
classification.ParentBoundaryPath = segments.Length == 3
? firstBoundaryPath
: BuildUncPath(segments, segments.Length - 1);
return classification;
}
if (Directory.Exists(firstBoundaryPath))
{
if (segments.Length == 3)
{
classification.Kind = eNtfsPathKind.DfsLink;
classification.BoundaryPath = normalizedPath;
classification.ParentBoundaryPath = firstBoundaryPath;
return classification;
}
classification.Kind = eNtfsPathKind.Folder;
classification.BoundaryPath = BuildUncPath(segments, 3);
classification.ParentBoundaryPath = segments.Length == 4
? BuildUncPath(segments, 3)
: BuildUncPath(segments, segments.Length - 1);
return classification;
}
return classification;
}
private string NormalizeUncPath(string path)
{
if (string.IsNullOrWhiteSpace(path))
return string.Empty;
var segments = path.Trim().Replace('/', '\\').Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
if (segments.Length == 0)
return string.Empty;
return @"\\" + string.Join("\\", segments);
}
private string[] GetUncSegments(string path)
{
var normalized = NormalizeUncPath(path);
return normalized.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
}
private string BuildUncPath(string[] segments, int segmentCount)
{
if (segments == null || segmentCount <= 0 || segments.Length < segmentCount)
return string.Empty;
return @"\\" + string.Join("\\", segments.Take(segmentCount));
}
private string GetDisplayName(string path)
{
var segments = GetUncSegments(path);
if (segments.Length == 0)
return string.Empty;
return segments.Last();
}
private HashSet<string> GetPublishedShareNames(string serverName)
{
HashSet<string> shares;
if (publishedShareCache.TryGetValue(serverName, out shares))
return shares;
shares = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
try
{
using (var connection = new cNetworkConnection(this.RootPath, this.Credential?.Identification, this.Credential?.Secret))
{
foreach (var share in connection.EnumNetShares(serverName))
{
if (!IsVisibleDiskShare(share))
continue;
shares.Add(share.shi1_netname);
}
}
}
catch (Exception ex)
{
LogException(ex);
}
publishedShareCache[serverName] = shares;
return shares;
}
private bool IsVisibleDiskShare(C4IT_IAM.SHARE_INFO_1 share)
{
if (string.IsNullOrWhiteSpace(share.shi1_netname))
return false;
if (share.shi1_netname.StartsWith("ERROR=", StringComparison.OrdinalIgnoreCase))
return false;
if (share.shi1_netname.EndsWith("$", StringComparison.OrdinalIgnoreCase))
return false;
var shareType = share.shi1_type & 0xFF;
return shareType == (uint)C4IT_IAM.SHARE_TYPE.STYPE_DISKTREE;
}
public override async Task<List<cLiamDataAreaBase>> getSecurityGroupsAsync(string groupFilter) public override async Task<List<cLiamDataAreaBase>> getSecurityGroupsAsync(string groupFilter)
{ {
@@ -393,7 +563,8 @@ namespace C4IT.LIAM
groupReadTag = GetRequiredCustomTag("Filesystem_GroupReadTag"), groupReadTag = GetRequiredCustomTag("Filesystem_GroupReadTag"),
groupTraverseTag = GetRequiredCustomTag("Filesystem_GroupTraverseTag"), groupTraverseTag = GetRequiredCustomTag("Filesystem_GroupTraverseTag"),
groupDLTag = requiresDomainLocalTag ? GetRequiredCustomTag("Filesystem_GroupDomainLocalTag") : string.Empty, groupDLTag = requiresDomainLocalTag ? GetRequiredCustomTag("Filesystem_GroupDomainLocalTag") : string.Empty,
groupGTag = GetRequiredCustomTag("Filesystem_GroupGlobalTag") groupGTag = GetRequiredCustomTag("Filesystem_GroupGlobalTag"),
allowExistingGroupWildcardMatch = IsAdditionalConfigurationEnabled("EnsureNtfsPermissionGroupsAllowRegexMatch")
}; };
foreach (var template in BuildSecurityGroupTemplates()) foreach (var template in BuildSecurityGroupTemplates())
@@ -402,6 +573,19 @@ namespace C4IT.LIAM
return engine; 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() private IEnumerable<IAM_SecurityGroupTemplate> BuildSecurityGroupTemplates()
{ {
var templates = new List<IAM_SecurityGroupTemplate>(); var templates = new List<IAM_SecurityGroupTemplate>();
@@ -689,7 +873,7 @@ namespace C4IT.LIAM
{ {
private readonly cNtfsResultBase Share = null; private readonly cNtfsResultBase Share = null;
public cLiamNtfsShare(cLiamProviderNtfs Provider, cNtfsResultBase Share) : public cLiamNtfsShare(cLiamProviderNtfs Provider, cNtfsResultBase Share, string parentPath = null) :
base(Provider) base(Provider)
{ {
this.Share = Share; this.Share = Share;
@@ -701,6 +885,8 @@ namespace C4IT.LIAM
this.DataType = eLiamDataAreaTypes.NtfsShare; this.DataType = eLiamDataAreaTypes.NtfsShare;
if (Directory.Exists(Share.Path)) if (Directory.Exists(Share.Path))
this.CreatedDate = new DirectoryInfo(Share.Path).CreationTimeUtc.ToString("s"); this.CreatedDate = new DirectoryInfo(Share.Path).CreationTimeUtc.ToString("s");
if (!string.IsNullOrWhiteSpace(parentPath))
this.ParentUID = cLiamNtfsFolder.GetUniqueDataAreaID(parentPath);
} }
internal async Task<List<cLiamDataAreaBase>> getFolders() internal async Task<List<cLiamDataAreaBase>> getFolders()
@@ -746,6 +932,30 @@ namespace C4IT.LIAM
} }
} }
public class cLiamNtfsDfsNamespaceRoot : cLiamNtfsPermissionDataAreaBase
{
private readonly cNtfsResultBase NamespaceRoot = null;
public cLiamNtfsDfsNamespaceRoot(cLiamProviderNtfs Provider, cNtfsResultBase NamespaceRoot) :
base(Provider)
{
this.NamespaceRoot = NamespaceRoot;
this.DisplayName = NamespaceRoot.Path.Split('\\').Last();
this.TechnicalName = NamespaceRoot.Path;
this.UID = cLiamNtfsFolder.GetUniqueDataAreaID(NamespaceRoot.Path);
this.Level = NamespaceRoot.Level;
this.DataType = eLiamDataAreaTypes.DfsNamespaceRoot;
if (Directory.Exists(NamespaceRoot.Path))
this.CreatedDate = new DirectoryInfo(NamespaceRoot.Path).CreationTimeUtc.ToString("s");
}
public override async Task<List<cLiamDataAreaBase>> getChildrenAsync(int Depth = -1)
{
await Task.Delay(0);
return new List<cLiamDataAreaBase>();
}
}
public class cLiamAdGroup : cLiamDataAreaBase public class cLiamAdGroup : cLiamDataAreaBase
{ {
public new readonly cLiamProviderNtfs Provider = null; public new readonly cLiamProviderNtfs Provider = null;
@@ -768,7 +978,7 @@ namespace C4IT.LIAM
{ {
public readonly cLiamNtfsShare Share = null; public readonly cLiamNtfsShare Share = null;
public readonly cLiamNtfsFolder NtfsRootFolder = null; public readonly cLiamNtfsFolder NtfsRootFolder = null;
public cLiamNtfsFolder(cLiamProviderNtfs Provider, cLiamNtfsShare share, cLiamNtfsFolder ntfsRootFolder, cNtfsResultFolder NtfsFolder) : base(Provider) public cLiamNtfsFolder(cLiamProviderNtfs Provider, cLiamNtfsShare share, cLiamNtfsFolder ntfsRootFolder, cNtfsResultFolder NtfsFolder, string parentPathOverride = null) : base(Provider)
{ {
var ntfsParent = NtfsFolder.Parent; var ntfsParent = NtfsFolder.Parent;
this.NtfsRootFolder = ntfsRootFolder; this.NtfsRootFolder = ntfsRootFolder;
@@ -787,6 +997,8 @@ namespace C4IT.LIAM
{ {
this.ParentUID = GetUniqueDataAreaID(this.Provider.RootPath); this.ParentUID = GetUniqueDataAreaID(this.Provider.RootPath);
} }
if (string.IsNullOrWhiteSpace(this.ParentUID) && !string.IsNullOrWhiteSpace(parentPathOverride))
this.ParentUID = GetUniqueDataAreaID(parentPathOverride);
} }
public static string GetUniqueDataAreaID(string fullPath) public static string GetUniqueDataAreaID(string fullPath)

View File

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

View File

@@ -23,6 +23,7 @@ namespace C4IT_IAM_Engine
public string domainName; public string domainName;
public string username; public string username;
public SecureString password; public SecureString password;
public bool AllowExistingGroupWildcardMatch;
public List<IAM_SecurityGroup> IAM_SecurityGroups; public List<IAM_SecurityGroup> IAM_SecurityGroups;
public string rootUID; public string rootUID;
@@ -208,6 +209,7 @@ namespace C4IT_IAM_Engine
{ {
Name = ownerGlobal.NamingTemplate, Name = ownerGlobal.NamingTemplate,
description = ownerGlobal.DescriptionTemplate, description = ownerGlobal.DescriptionTemplate,
WildcardPattern = ownerGlobal.WildcardTemplate,
technicalName = "CN=" + ownerGlobal.NamingTemplate + "," + ouPath, technicalName = "CN=" + ownerGlobal.NamingTemplate + "," + ouPath,
targetTyp = (int)IAM_TargetType.FileSystem, targetTyp = (int)IAM_TargetType.FileSystem,
@@ -221,6 +223,7 @@ namespace C4IT_IAM_Engine
{ {
Name = writeGlobal.NamingTemplate, Name = writeGlobal.NamingTemplate,
description = writeGlobal.DescriptionTemplate, description = writeGlobal.DescriptionTemplate,
WildcardPattern = writeGlobal.WildcardTemplate,
technicalName = "CN=" + writeGlobal.NamingTemplate + "," + ouPath, technicalName = "CN=" + writeGlobal.NamingTemplate + "," + ouPath,
targetTyp = (int)IAM_TargetType.FileSystem, targetTyp = (int)IAM_TargetType.FileSystem,
@@ -234,6 +237,7 @@ namespace C4IT_IAM_Engine
{ {
Name = readGlobal.NamingTemplate, Name = readGlobal.NamingTemplate,
description = readGlobal.DescriptionTemplate, description = readGlobal.DescriptionTemplate,
WildcardPattern = readGlobal.WildcardTemplate,
technicalName = "CN=" + readGlobal.NamingTemplate + "," + ouPath, technicalName = "CN=" + readGlobal.NamingTemplate + "," + ouPath,
targetTyp = (int)IAM_TargetType.FileSystem, targetTyp = (int)IAM_TargetType.FileSystem,
@@ -251,6 +255,7 @@ namespace C4IT_IAM_Engine
{ {
Name = ownerDL.NamingTemplate, Name = ownerDL.NamingTemplate,
description = ownerDL.DescriptionTemplate, description = ownerDL.DescriptionTemplate,
WildcardPattern = ownerDL.WildcardTemplate,
technicalName = "CN=" + ownerDL.NamingTemplate + "," + ouPath, technicalName = "CN=" + ownerDL.NamingTemplate + "," + ouPath,
targetTyp = (int)IAM_TargetType.FileSystem, targetTyp = (int)IAM_TargetType.FileSystem,
@@ -265,6 +270,7 @@ namespace C4IT_IAM_Engine
{ {
Name = writeDL.NamingTemplate, Name = writeDL.NamingTemplate,
description = writeDL.DescriptionTemplate, description = writeDL.DescriptionTemplate,
WildcardPattern = writeDL.WildcardTemplate,
technicalName = "CN=" + writeDL.NamingTemplate + "," + ouPath, technicalName = "CN=" + writeDL.NamingTemplate + "," + ouPath,
targetTyp = (int)IAM_TargetType.FileSystem, targetTyp = (int)IAM_TargetType.FileSystem,
@@ -279,6 +285,7 @@ namespace C4IT_IAM_Engine
{ {
Name = readDL.NamingTemplate, Name = readDL.NamingTemplate,
description = readDL.DescriptionTemplate, description = readDL.DescriptionTemplate,
WildcardPattern = readDL.WildcardTemplate,
technicalName = "CN=" + readDL.NamingTemplate + "," + ouPath, technicalName = "CN=" + readDL.NamingTemplate + "," + ouPath,
targetTyp = (int)IAM_TargetType.FileSystem, targetTyp = (int)IAM_TargetType.FileSystem,
@@ -293,6 +300,7 @@ namespace C4IT_IAM_Engine
secGroup.description = secGroup.description.ReplaceLoopTag(0); secGroup.description = secGroup.description.ReplaceLoopTag(0);
secGroup.Name = secGroup.Name.ReplaceLoopTag(loop); secGroup.Name = secGroup.Name.ReplaceLoopTag(loop);
secGroup.technicalName = secGroup.technicalName.ReplaceLoopTag(loop); secGroup.technicalName = secGroup.technicalName.ReplaceLoopTag(loop);
secGroup.WildcardPattern = secGroup.WildcardPattern.ReplaceLoopTag(loop);
DefaultLogger.LogEntry(LogLevels.Debug, $"Security group generated: {secGroup.technicalName}"); DefaultLogger.LogEntry(LogLevels.Debug, $"Security group generated: {secGroup.technicalName}");
} }
} }
@@ -398,6 +406,92 @@ namespace C4IT_IAM_Engine
return groupEntry; 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) private static bool HasMember(PropertyValueCollection members, string distinguishedName)
{ {
foreach (var member in members) foreach (var member in members)
@@ -450,13 +544,16 @@ namespace C4IT_IAM_Engine
LogMethodBegin(MethodBase.GetCurrentMethod()); LogMethodBegin(MethodBase.GetCurrentMethod());
try try
{ {
secGroup.ReusedExistingEntry = false;
var existingGroup = FindGroupEntry(secGroup.Name); var existingGroup = FindGroupEntry(secGroup.Name);
if (existingGroup == null && AllowExistingGroupWildcardMatch)
existingGroup = FindGroupEntryByWildcard(ouPath, secGroup.WildcardPattern);
if (existingGroup == null) if (existingGroup == null)
return CreateADGroup(ouPath, secGroup, users); return CreateADGroup(ouPath, secGroup, users);
AddMissingMembers(existingGroup, secGroup, users); AddMissingMembers(existingGroup, secGroup, users);
var objectid = getSID(existingGroup); ApplyExistingGroup(secGroup, existingGroup);
secGroup.UID = objectid;
return existingGroup; return existingGroup;
} }
catch (Exception E) catch (Exception E)
@@ -475,6 +572,7 @@ namespace C4IT_IAM_Engine
LogMethodBegin(MethodBase.GetCurrentMethod()); LogMethodBegin(MethodBase.GetCurrentMethod());
try try
{ {
secGroup.ReusedExistingEntry = false;
if (!GroupAllreadyExisting(secGroup.Name.ToUpper())) if (!GroupAllreadyExisting(secGroup.Name.ToUpper()))
{ {
@@ -518,9 +616,8 @@ namespace C4IT_IAM_Engine
DirectoryEntry e = FindGroupEntry(secGroup.Name); DirectoryEntry e = FindGroupEntry(secGroup.Name);
if (e == null) if (e == null)
return null; return null;
var objectid = getSID(e);
secGroup.UID = objectid;
AddMissingMembers(e, secGroup, users); AddMissingMembers(e, secGroup, users);
ApplyExistingGroup(secGroup, e);
return e; return e;
} }
return null; return null;
@@ -588,6 +685,8 @@ namespace C4IT_IAM_Engine
public string UID; public string UID;
public string Parent = ""; public string Parent = "";
public string description; public string description;
public string WildcardPattern;
public bool ReusedExistingEntry;
public List<IAM_SecurityGroup> memberGroups; public List<IAM_SecurityGroup> memberGroups;
public string Name; public string Name;
public string technicalName; public string technicalName;

View File

@@ -628,7 +628,7 @@ namespace LiamWorkflowActivities
if (!IsAdditionalConfigurationEnabled(providerEntry.Provider, "EnsureNtfsPermissionGroups")) if (!IsAdditionalConfigurationEnabled(providerEntry.Provider, "EnsureNtfsPermissionGroups"))
return true; return true;
foreach (var ntfsArea in dataAreas.OfType<cLiamNtfsPermissionDataAreaBase>()) foreach (var ntfsArea in dataAreas.OfType<cLiamNtfsFolder>())
{ {
var folderPath = ntfsArea.TechnicalName; var folderPath = ntfsArea.TechnicalName;
if (string.IsNullOrWhiteSpace(folderPath)) if (string.IsNullOrWhiteSpace(folderPath))