Safeguard NTFS AD group reuse
This commit is contained in:
@@ -144,6 +144,8 @@ namespace C4IT.LIAM
|
||||
private const string AdditionalConfigurationGroupNameSanitizeReplacementKey = "NtfsGroupNameSanitizeReplacement";
|
||||
private const string AdditionalConfigurationPreserveAdGroupNameCaseKey = "PreserveNtfsAdGroupNameCase";
|
||||
private const string AdditionalConfigurationAdDomainControllersKey = "NtfsAdDomainControllers";
|
||||
private const string AdditionalConfigurationAdGroupReuseModeKey = "NtfsAdGroupReuseMode";
|
||||
private const string AdditionalConfigurationAdGroupMarkerBackfillKey = "NtfsAdGroupMarkerBackfill";
|
||||
public readonly cNtfsBase ntfsBase = new cNtfsBase();
|
||||
public readonly cActiveDirectoryBase activeDirectoryBase = new cActiveDirectoryBase();
|
||||
private readonly Dictionary<string, HashSet<string>> publishedShareCache = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase);
|
||||
@@ -1394,6 +1396,8 @@ namespace C4IT.LIAM
|
||||
engine.CanManagePermissionsForPath = IsPermissionManagedFolderPath;
|
||||
engine.CanManageTraversePermissionsForPath = IsTraversePermissionManagedPath;
|
||||
engine.forceStrictAdGroupNames = IsAdditionalConfigurationEnabled("ForceStrictAdGroupNames");
|
||||
engine.adGroupReuseMode = GetAdGroupReuseMode();
|
||||
engine.adGroupMarkerBackfill = IsAdditionalConfigurationEnabled(AdditionalConfigurationAdGroupMarkerBackfillKey);
|
||||
engine.groupNameSanitizeReplacement = GetAdditionalConfigurationValueOrDefault(
|
||||
AdditionalConfigurationGroupNameSanitizeReplacementKey,
|
||||
Helper.DefaultGroupNameSanitizeReplacement);
|
||||
@@ -1458,6 +1462,25 @@ namespace C4IT.LIAM
|
||||
return rawValue == null ? string.Empty : rawValue.Trim();
|
||||
}
|
||||
|
||||
private SecurityGroupReuseMode GetAdGroupReuseMode()
|
||||
{
|
||||
var value = GetAdditionalConfigurationValue(AdditionalConfigurationAdGroupReuseModeKey);
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
return SecurityGroupReuseMode.Safe;
|
||||
|
||||
if (value.Equals("Safe", StringComparison.OrdinalIgnoreCase))
|
||||
return SecurityGroupReuseMode.Safe;
|
||||
|
||||
if (value.Equals("Name", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
LogEntry("NtfsAdGroupReuseMode=Name is active. Existing AD groups may be reused by name without ACL/marker validation.", LogLevels.Warning);
|
||||
return SecurityGroupReuseMode.Name;
|
||||
}
|
||||
|
||||
LogEntry($"AdditionalConfiguration '{AdditionalConfigurationAdGroupReuseModeKey}' has invalid value '{value}'. Defaulting to Safe.", LogLevels.Warning);
|
||||
return SecurityGroupReuseMode.Safe;
|
||||
}
|
||||
|
||||
private string GetEffectiveTraverseBoundaryPath()
|
||||
{
|
||||
var configuredBoundaryPath = GetAdditionalConfigurationValue(AdditionalConfigurationTraverseBoundaryPathKey);
|
||||
|
||||
@@ -57,6 +57,8 @@ namespace C4IT_IAM_SET
|
||||
public Func<string, bool> CanManageTraversePermissionsForPath;
|
||||
public string traverseBoundaryPath;
|
||||
public bool forceStrictAdGroupNames;
|
||||
public SecurityGroupReuseMode adGroupReuseMode = SecurityGroupReuseMode.Safe;
|
||||
public bool adGroupMarkerBackfill;
|
||||
public string groupNameSanitizeReplacement = Helper.DefaultGroupNameSanitizeReplacement;
|
||||
public bool preserveAdGroupNameCase;
|
||||
public bool WhatIf;
|
||||
@@ -323,7 +325,9 @@ namespace C4IT_IAM_SET
|
||||
effectiveDomainController = effectiveDomainController,
|
||||
password = password,
|
||||
ForceStrictAdGroupNames = forceStrictAdGroupNames,
|
||||
PreserveAdGroupNameCase = preserveAdGroupNameCase
|
||||
PreserveAdGroupNameCase = preserveAdGroupNameCase,
|
||||
ReuseMode = adGroupReuseMode,
|
||||
MarkerBackfill = adGroupMarkerBackfill
|
||||
};
|
||||
}
|
||||
|
||||
@@ -934,7 +938,9 @@ namespace C4IT_IAM_SET
|
||||
Name = traverseNameTemplate.ReplaceLoopTag(loop),
|
||||
description = traverseDescriptionTemplate.ReplaceLoopTag(loop),
|
||||
technicalName = "CN=" + traverseNameTemplate.ReplaceLoopTag(loop) + "," + groupOUPath,
|
||||
Scope = traverseGroupTemplate.Scope
|
||||
securityGroupType = SecurityGroupType.Traverse,
|
||||
Scope = traverseGroupTemplate.Scope,
|
||||
MarkerPath = parent.FullName
|
||||
};
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Erstellte TraverseGroup: {newTraverseGroup.Name} (Loop: {loop})");
|
||||
loop++;
|
||||
|
||||
@@ -26,9 +26,13 @@ namespace C4IT_IAM_Engine
|
||||
public SecureString password;
|
||||
public bool ForceStrictAdGroupNames;
|
||||
public bool PreserveAdGroupNameCase;
|
||||
public SecurityGroupReuseMode ReuseMode = SecurityGroupReuseMode.Safe;
|
||||
public bool MarkerBackfill;
|
||||
|
||||
public List<IAM_SecurityGroup> IAM_SecurityGroups;
|
||||
public string rootUID;
|
||||
private const string MarkerAttributeName = "info";
|
||||
private const string MarkerPrefix = "LIAM;Provider=Ntfs;";
|
||||
public SecurityGroups()
|
||||
{
|
||||
IAM_SecurityGroups = new List<IAM_SecurityGroup>();
|
||||
@@ -269,9 +273,11 @@ namespace C4IT_IAM_Engine
|
||||
WildcardPattern = ownerGlobal.WildcardTemplate,
|
||||
|
||||
technicalName = "CN=" + ownerGlobal.NamingTemplate + "," + ouPath,
|
||||
securityGroupType = SecurityGroupType.Owner,
|
||||
targetTyp = (int)IAM_TargetType.FileSystem,
|
||||
rights = (FileSystemRights)ownerACLPermission,
|
||||
Scope = GroupScope.Global
|
||||
Scope = GroupScope.Global,
|
||||
MarkerPath = newFolderPath
|
||||
};
|
||||
IAM_SecurityGroups.Add(osecGroup);
|
||||
|
||||
@@ -283,9 +289,11 @@ namespace C4IT_IAM_Engine
|
||||
WildcardPattern = writeGlobal.WildcardTemplate,
|
||||
|
||||
technicalName = "CN=" + writeGlobal.NamingTemplate + "," + ouPath,
|
||||
securityGroupType = SecurityGroupType.Write,
|
||||
targetTyp = (int)IAM_TargetType.FileSystem,
|
||||
rights = (FileSystemRights)writeACLPermission,
|
||||
Scope = GroupScope.Global
|
||||
Scope = GroupScope.Global,
|
||||
MarkerPath = newFolderPath
|
||||
};
|
||||
IAM_SecurityGroups.Add(wsecGroup);
|
||||
|
||||
@@ -297,9 +305,11 @@ namespace C4IT_IAM_Engine
|
||||
WildcardPattern = readGlobal.WildcardTemplate,
|
||||
|
||||
technicalName = "CN=" + readGlobal.NamingTemplate + "," + ouPath,
|
||||
securityGroupType = SecurityGroupType.Read,
|
||||
targetTyp = (int)IAM_TargetType.FileSystem,
|
||||
rights = (FileSystemRights)readACLPermission,
|
||||
Scope = GroupScope.Global
|
||||
Scope = GroupScope.Global,
|
||||
MarkerPath = newFolderPath
|
||||
};
|
||||
IAM_SecurityGroups.Add(rsecGroup);
|
||||
|
||||
@@ -315,9 +325,11 @@ namespace C4IT_IAM_Engine
|
||||
WildcardPattern = ownerDL.WildcardTemplate,
|
||||
|
||||
technicalName = "CN=" + ownerDL.NamingTemplate + "," + ouPath,
|
||||
securityGroupType = SecurityGroupType.Owner,
|
||||
targetTyp = (int)IAM_TargetType.FileSystem,
|
||||
rights = (FileSystemRights)ownerACLPermission,
|
||||
Scope = GroupScope.Local
|
||||
Scope = GroupScope.Local,
|
||||
MarkerPath = newFolderPath
|
||||
};
|
||||
osecDLGroup.memberGroups.Add(osecGroup);
|
||||
IAM_SecurityGroups.Add(osecDLGroup);
|
||||
@@ -330,9 +342,11 @@ namespace C4IT_IAM_Engine
|
||||
WildcardPattern = writeDL.WildcardTemplate,
|
||||
|
||||
technicalName = "CN=" + writeDL.NamingTemplate + "," + ouPath,
|
||||
securityGroupType = SecurityGroupType.Write,
|
||||
targetTyp = (int)IAM_TargetType.FileSystem,
|
||||
rights = (FileSystemRights)writeACLPermission,
|
||||
Scope = GroupScope.Local
|
||||
Scope = GroupScope.Local,
|
||||
MarkerPath = newFolderPath
|
||||
};
|
||||
wsecDLGroup.memberGroups.Add(wsecGroup);
|
||||
IAM_SecurityGroups.Add(wsecDLGroup);
|
||||
@@ -345,9 +359,11 @@ namespace C4IT_IAM_Engine
|
||||
WildcardPattern = readDL.WildcardTemplate,
|
||||
|
||||
technicalName = "CN=" + readDL.NamingTemplate + "," + ouPath,
|
||||
securityGroupType = SecurityGroupType.Read,
|
||||
targetTyp = (int)IAM_TargetType.FileSystem,
|
||||
rights = (FileSystemRights)readACLPermission,
|
||||
Scope = GroupScope.Local
|
||||
Scope = GroupScope.Local,
|
||||
MarkerPath = newFolderPath
|
||||
};
|
||||
rsecDLGroup.memberGroups.Add(rsecGroup);
|
||||
IAM_SecurityGroups.Add(rsecDLGroup);
|
||||
@@ -661,6 +677,168 @@ namespace C4IT_IAM_Engine
|
||||
group.CommitChanges();
|
||||
}
|
||||
|
||||
private static string NormalizeMarkerPath(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return string.Empty;
|
||||
|
||||
return path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar).TrimEnd(Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
private static string GetMarkerScope(GroupScope scope)
|
||||
{
|
||||
return scope == GroupScope.Local ? "DomainLocal" : "Global";
|
||||
}
|
||||
|
||||
private static string BuildMarker(IAM_SecurityGroup secGroup)
|
||||
{
|
||||
return $"{MarkerPrefix}Path={NormalizeMarkerPath(secGroup.MarkerPath)};Role={secGroup.securityGroupType};Scope={GetMarkerScope(secGroup.Scope)}";
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> ParseMarker(string marker)
|
||||
{
|
||||
var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
if (string.IsNullOrWhiteSpace(marker) || !marker.StartsWith(MarkerPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
return result;
|
||||
|
||||
foreach (var part in marker.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
var separatorIndex = part.IndexOf('=');
|
||||
if (separatorIndex <= 0)
|
||||
continue;
|
||||
|
||||
result[part.Substring(0, separatorIndex).Trim()] = part.Substring(separatorIndex + 1).Trim();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetLiamMarkers(string info)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(info))
|
||||
return Enumerable.Empty<string>();
|
||||
|
||||
return info
|
||||
.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(i => i.Trim())
|
||||
.Where(i => i.StartsWith(MarkerPrefix, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static bool IsMatchingMarker(string marker, IAM_SecurityGroup secGroup)
|
||||
{
|
||||
var values = ParseMarker(marker);
|
||||
if (values.Count == 0)
|
||||
return false;
|
||||
|
||||
return values.TryGetValue("Provider", out var provider)
|
||||
&& string.Equals(provider, "Ntfs", StringComparison.OrdinalIgnoreCase)
|
||||
&& values.TryGetValue("Path", out var markerPath)
|
||||
&& string.Equals(NormalizeMarkerPath(markerPath), NormalizeMarkerPath(secGroup.MarkerPath), StringComparison.OrdinalIgnoreCase)
|
||||
&& values.TryGetValue("Role", out var role)
|
||||
&& string.Equals(role, secGroup.securityGroupType.ToString(), StringComparison.OrdinalIgnoreCase)
|
||||
&& values.TryGetValue("Scope", out var scope)
|
||||
&& string.Equals(scope, GetMarkerScope(secGroup.Scope), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private bool TryReadInfoAttribute(DirectoryEntry group, out string info)
|
||||
{
|
||||
info = string.Empty;
|
||||
if (group == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
group.RefreshCache(new[] { MarkerAttributeName });
|
||||
if (group.Properties.Contains(MarkerAttributeName) && group.Properties[MarkerAttributeName].Count > 0)
|
||||
info = group.Properties[MarkerAttributeName].Value?.ToString() ?? string.Empty;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
DefaultLogger.LogEntry(LogLevels.Warning, $"AD group marker attribute '{MarkerAttributeName}' cannot be read for '{group.Path}'. Marker-based reuse is not available. {E.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasMatchingMarker(DirectoryEntry group, IAM_SecurityGroup secGroup)
|
||||
{
|
||||
if (!TryReadInfoAttribute(group, out var info))
|
||||
return false;
|
||||
|
||||
return GetLiamMarkers(info).Any(marker => IsMatchingMarker(marker, secGroup));
|
||||
}
|
||||
|
||||
private bool TryWriteMarker(DirectoryEntry group, IAM_SecurityGroup secGroup, bool onlyIfNoLiamMarker)
|
||||
{
|
||||
if (group == null)
|
||||
return false;
|
||||
|
||||
if (!TryReadInfoAttribute(group, out var info))
|
||||
return false;
|
||||
|
||||
var existingMarkers = GetLiamMarkers(info).ToList();
|
||||
if (existingMarkers.Any(marker => IsMatchingMarker(marker, secGroup)))
|
||||
return true;
|
||||
|
||||
if (onlyIfNoLiamMarker && existingMarkers.Count > 0)
|
||||
{
|
||||
DefaultLogger.LogEntry(LogLevels.Warning, $"AD group '{secGroup.Name}' already has a LIAM marker, but it does not match path '{secGroup.MarkerPath}' and role '{secGroup.securityGroupType}'. Marker backfill is skipped.");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var marker = BuildMarker(secGroup);
|
||||
var newInfo = string.IsNullOrWhiteSpace(info)
|
||||
? marker
|
||||
: info.TrimEnd('\r', '\n') + Environment.NewLine + marker;
|
||||
group.Properties[MarkerAttributeName].Value = newInfo;
|
||||
group.CommitChanges();
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"AD group marker written to '{secGroup.Name}': {marker}");
|
||||
return true;
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
DefaultLogger.LogEntry(LogLevels.Warning, $"AD group marker attribute '{MarkerAttributeName}' cannot be written for '{secGroup.Name}'. {E.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private DirectoryEntry ValidateExistingGroupReuse(DirectoryEntry existingGroup, IAM_SecurityGroup secGroup, string folderPath, bool linkedFromFolderAcl)
|
||||
{
|
||||
if (existingGroup == null)
|
||||
return null;
|
||||
|
||||
if (ReuseMode == SecurityGroupReuseMode.Name)
|
||||
{
|
||||
if (!linkedFromFolderAcl && !HasMatchingMarker(existingGroup, secGroup))
|
||||
DefaultLogger.LogEntry(LogLevels.Warning, $"AD group '{secGroup.Name}' is reused by name because NtfsAdGroupReuseMode=Name is active. This bypasses ACL/marker validation for '{secGroup.MarkerPath}'.");
|
||||
|
||||
ApplyExistingGroup(secGroup, existingGroup);
|
||||
return existingGroup;
|
||||
}
|
||||
|
||||
if (linkedFromFolderAcl)
|
||||
{
|
||||
if (MarkerBackfill)
|
||||
TryWriteMarker(existingGroup, secGroup, true);
|
||||
|
||||
ApplyExistingGroup(secGroup, existingGroup);
|
||||
return existingGroup;
|
||||
}
|
||||
|
||||
if (HasMatchingMarker(existingGroup, secGroup))
|
||||
{
|
||||
ApplyExistingGroup(secGroup, existingGroup);
|
||||
return existingGroup;
|
||||
}
|
||||
|
||||
var message = $"Existing AD group '{secGroup.Name}' was found by name for '{folderPath ?? secGroup.MarkerPath}', but it is not linked on the folder ACL and has no matching LIAM marker. Reuse is blocked in NtfsAdGroupReuseMode=Safe. Set NtfsAdGroupReuseMode=Name to allow legacy name-based reuse.";
|
||||
DefaultLogger.LogEntry(LogLevels.Warning, message);
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
public DirectoryEntry EnsureADGroup(string ouPath, IAM_SecurityGroup secGroup, List<UserPrincipal> users, string folderPath = null)
|
||||
{
|
||||
LogMethodBegin(MethodBase.GetCurrentMethod());
|
||||
@@ -668,8 +846,12 @@ namespace C4IT_IAM_Engine
|
||||
{
|
||||
secGroup.CreatedNewEntry = false;
|
||||
DirectoryEntry existingGroup = null;
|
||||
var linkedFromFolderAcl = false;
|
||||
if (!ForceStrictAdGroupNames)
|
||||
{
|
||||
existingGroup = FindGroupEntryFromFolderAcl(folderPath, secGroup.WildcardPattern);
|
||||
linkedFromFolderAcl = existingGroup != null;
|
||||
}
|
||||
|
||||
if (existingGroup == null)
|
||||
existingGroup = FindGroupEntry(secGroup.Name);
|
||||
@@ -680,8 +862,8 @@ namespace C4IT_IAM_Engine
|
||||
if (existingGroup == null)
|
||||
return CreateADGroup(ouPath, secGroup, users);
|
||||
|
||||
existingGroup = ValidateExistingGroupReuse(existingGroup, secGroup, folderPath, linkedFromFolderAcl);
|
||||
AddMissingMembers(existingGroup, secGroup, users);
|
||||
ApplyExistingGroup(secGroup, existingGroup);
|
||||
return existingGroup;
|
||||
}
|
||||
catch (Exception E)
|
||||
@@ -702,8 +884,12 @@ namespace C4IT_IAM_Engine
|
||||
{
|
||||
secGroup.CreatedNewEntry = false;
|
||||
DirectoryEntry existingGroup = null;
|
||||
var linkedFromFolderAcl = false;
|
||||
if (!ForceStrictAdGroupNames)
|
||||
{
|
||||
existingGroup = FindGroupEntryFromFolderAcl(folderPath, secGroup.WildcardPattern);
|
||||
linkedFromFolderAcl = existingGroup != null;
|
||||
}
|
||||
|
||||
if (existingGroup == null)
|
||||
existingGroup = FindGroupEntry(secGroup.Name);
|
||||
@@ -714,8 +900,7 @@ namespace C4IT_IAM_Engine
|
||||
if (existingGroup == null)
|
||||
return null;
|
||||
|
||||
ApplyExistingGroup(secGroup, existingGroup);
|
||||
return existingGroup;
|
||||
return ValidateExistingGroupReuse(existingGroup, secGroup, folderPath, linkedFromFolderAcl);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
@@ -738,55 +923,45 @@ namespace C4IT_IAM_Engine
|
||||
secGroup.Name = groupName;
|
||||
secGroup.technicalName = "CN=" + groupName + "," + ouPath;
|
||||
|
||||
if (!GroupAllreadyExisting(groupName))
|
||||
if (GroupAllreadyExisting(groupName))
|
||||
throw new InvalidOperationException($"AD group '{groupName}' already exists. Create mode does not reuse existing groups.");
|
||||
|
||||
DirectoryEntry entry = new DirectoryEntry("LDAP://" + GetLdapServer() + "/" + ouPath, username, new NetworkCredential("", password).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Creating ad entry with CN / sAmAccountName: {groupName}");
|
||||
DirectoryEntry group = entry.Children.Add("CN=" + groupName, "group");
|
||||
group.Properties["sAmAccountName"].Value = groupName;
|
||||
if (users != null && secGroup.Scope == GroupScope.Global)
|
||||
{
|
||||
|
||||
DirectoryEntry entry = new DirectoryEntry("LDAP://" + GetLdapServer() + "/" + ouPath, username, new NetworkCredential("", password).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Creating ad entry with CN / sAmAccountName: {groupName}");
|
||||
DirectoryEntry group = entry.Children.Add("CN=" + groupName, "group");
|
||||
group.Properties["sAmAccountName"].Value = groupName;
|
||||
if (users != null && secGroup.Scope == GroupScope.Global)
|
||||
foreach (var user in users)
|
||||
{
|
||||
foreach (var user in users)
|
||||
{
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Adding member: {user.DistinguishedName}");
|
||||
group.Properties["member"].Add(user.DistinguishedName);
|
||||
}
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Adding member: {user.DistinguishedName}");
|
||||
group.Properties["member"].Add(user.DistinguishedName);
|
||||
}
|
||||
if(!String.IsNullOrEmpty(secGroup.description))
|
||||
{
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Setting description: {secGroup.description}");
|
||||
group.Properties["description"].Value = secGroup.description;
|
||||
}
|
||||
var groupType = secGroup.Scope == GroupScope.Global ? GroupScopeValues.Global : GroupScopeValues.Local;
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Setting groupType to: {groupType}");
|
||||
group.Properties["groupType"].Value = groupType;
|
||||
if (secGroup.Scope == GroupScope.Local)
|
||||
foreach (var iGroup in secGroup.memberGroups)
|
||||
{
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Adding member: {iGroup.technicalName}");
|
||||
group.Properties["member"].Add(iGroup.technicalName);
|
||||
}
|
||||
|
||||
group.CommitChanges();
|
||||
DirectoryEntry ent = new DirectoryEntry("LDAP://" + GetLdapServer() + "/" + "CN=" + groupName + "," + ouPath, username, new NetworkCredential("", password).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
|
||||
|
||||
var objectid = SecurityGroups.getSID(ent);
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Security group created in ad: {secGroup.technicalName}");
|
||||
secGroup.UID = objectid;
|
||||
secGroup.CreatedNewEntry = true;
|
||||
return ent;
|
||||
}
|
||||
else
|
||||
if(!String.IsNullOrEmpty(secGroup.description))
|
||||
{
|
||||
DirectoryEntry e = FindGroupEntry(secGroup.Name);
|
||||
if (e == null)
|
||||
return null;
|
||||
AddMissingMembers(e, secGroup, users);
|
||||
ApplyExistingGroup(secGroup, e);
|
||||
return e;
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Setting description: {secGroup.description}");
|
||||
group.Properties["description"].Value = secGroup.description;
|
||||
}
|
||||
return null;
|
||||
var groupType = secGroup.Scope == GroupScope.Global ? GroupScopeValues.Global : GroupScopeValues.Local;
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Setting groupType to: {groupType}");
|
||||
group.Properties["groupType"].Value = groupType;
|
||||
if (secGroup.Scope == GroupScope.Local)
|
||||
foreach (var iGroup in secGroup.memberGroups)
|
||||
{
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Adding member: {iGroup.technicalName}");
|
||||
group.Properties["member"].Add(iGroup.technicalName);
|
||||
}
|
||||
|
||||
group.CommitChanges();
|
||||
DirectoryEntry ent = new DirectoryEntry("LDAP://" + GetLdapServer() + "/" + "CN=" + groupName + "," + ouPath, username, new NetworkCredential("", password).Password, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
|
||||
|
||||
var objectid = SecurityGroups.getSID(ent);
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"Security group created in ad: {secGroup.technicalName}");
|
||||
secGroup.UID = objectid;
|
||||
secGroup.CreatedNewEntry = true;
|
||||
TryWriteMarker(ent, secGroup, false);
|
||||
return ent;
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
@@ -865,11 +1040,17 @@ namespace C4IT_IAM_Engine
|
||||
public int targetTyp;
|
||||
public GroupScope Scope;
|
||||
public FileSystemRights rights;
|
||||
public string MarkerPath;
|
||||
public IAM_SecurityGroup()
|
||||
{
|
||||
memberGroups = new List<IAM_SecurityGroup>();
|
||||
}
|
||||
}
|
||||
public enum SecurityGroupReuseMode
|
||||
{
|
||||
Safe,
|
||||
Name
|
||||
}
|
||||
public enum SecurityGroupType
|
||||
{
|
||||
[XmlEnum(Name = "0")]
|
||||
|
||||
@@ -215,6 +215,8 @@ Im Diagnose-JSON erscheinen diese Werte unter `AdditionalConfiguration`. Paramet
|
||||
| `NtfsGroupNameSanitizeReplacement` | Zeichenfolge, z. B. `_`, `.`, leer, `none`, `remove`, `<empty>` | Legt fest, womit ungueltige Zeichen in dynamischen gruppennamenrelevanten Pfadbestandteilen ersetzt werden. Standard ist `_`. Mit leerem Wert oder `none`/`remove`/`<empty>` werden ungueltige Zeichen entfernt und Pfadsegmente ohne Trennzeichen verbunden. |
|
||||
| `PreserveNtfsAdGroupNameCase` | `true`, `1`, `yes` | Unterbindet das automatische Uppercase fuer generierte NTFS-AD-Gruppennamen. Ohne diesen Parameter werden generierte Gruppennamen wie bisher in Grossbuchstaben erzeugt. |
|
||||
| `ForceStrictAdGroupNames` | `true`, `1`, `yes` | Erzwingt strikte AD-Gruppennamen. Wildcard-/ACL-basierte Wiederverwendung abweichender bestehender Gruppen wird damit eingeschraenkt; es werden nur exakt passende konfigurierte oder generierte Namen verwendet. |
|
||||
| `NtfsAdGroupReuseMode` | `Safe`, `Name` | Steuert die Wiederverwendung vorhandener AD-Gruppen im automatischen Ensure. Standard ist `Safe`: vorhandene Gruppen werden nur wiederverwendet, wenn sie bereits passend auf der Ordner-ACL liegen oder einen passenden LIAM-Marker im AD-Attribut `info` besitzen. `Name` erlaubt die Legacy-Wiederverwendung nur anhand des Namens und wird als unsicherer Modus geloggt. |
|
||||
| `NtfsAdGroupMarkerBackfill` | `true`, `1`, `yes` | Ergaenzt bei sicher wiederverwendeten, ACL-verknuepften Gruppen einen LIAM-Marker im AD-Attribut `info`, sofern das Attribut les- und schreibbar ist. |
|
||||
| `NtfsAdDomainControllers` | Kommagetrennte DC-Liste, z. B. `dc01.contoso.local,dc02.contoso.local` | Pinnt NTFS-AD-Operationen auf einen Domain Controller. Der erste erreichbare DC wird verwendet. Wenn kein Eintrag erreichbar ist oder der Parameter fehlt, wird der PDC Emulator verwendet; danach faellt der Code auf die normale Domain-Locator-Logik zurueck. Der ausgewaehlte DC wird im Debug-Log protokolliert. |
|
||||
|
||||
Beispiele:
|
||||
|
||||
Reference in New Issue
Block a user