diff --git a/LiamNtfs/C4IT.LIAM.Ntfs.cs b/LiamNtfs/C4IT.LIAM.Ntfs.cs index 3dd44a5..7411f71 100644 --- a/LiamNtfs/C4IT.LIAM.Ntfs.cs +++ b/LiamNtfs/C4IT.LIAM.Ntfs.cs @@ -1258,6 +1258,16 @@ namespace C4IT.LIAM }); } + string levelSkipReason; + if (!IsLevelManagedPath(classification, traverseLevelRange, "traverse group ensure", out levelSkipReason)) + { + return Task.FromResult(new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString()) + { + resultErrorId = 30008, + resultMessage = levelSkipReason + }); + } + if (!IsTraversePermissionCandidatePath(folderPath)) { return Task.FromResult(new ResultToken(System.Reflection.MethodBase.GetCurrentMethod().ToString()) diff --git a/LiamNtfs/C4IT_IAM_SET/DataArea_FileSystem.cs b/LiamNtfs/C4IT_IAM_SET/DataArea_FileSystem.cs index 7aaecb3..d01ae81 100644 --- a/LiamNtfs/C4IT_IAM_SET/DataArea_FileSystem.cs +++ b/LiamNtfs/C4IT_IAM_SET/DataArea_FileSystem.cs @@ -688,7 +688,7 @@ namespace C4IT_IAM_SET var folderName = sanitizedSegments.Length > 0 ? sanitizedSegments[sanitizedSegments.Length - 1] : Helper.SanitizePathSegment(Path.GetFileName(parent.FullName.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)), groupNameSanitizeReplacement); - var traverseTags = GetTraverseReplacementTags(parent.FullName); + var traverseTags = GetTraverseReplacementTags(parent.FullName, traverseGroupTemplate.Scope); var rootContext = Helper.GetRootPathTemplateContext(baseFolder, groupNameSanitizeReplacement); var boundedTraverseContext = Helper.GetBoundedAdGroupTemplateContext( traverseGroupTemplate.NamingTemplate, @@ -780,18 +780,6 @@ namespace C4IT_IAM_SET if (parentTraverseGroup != null) parentTraverseGroupName = parentTraverseGroup.Name; - if (parentTraverseGroup == null && hasTraverseWildcard && !forceStrictAdGroupNames) - { - parentTraverseGroup = FindTraverseGroupByWildcard(domainContext, traverseRegex); - if (parentTraverseGroup != null) - { - parentTraverseGroupName = parentTraverseGroup.Name; - resultToken.reusedGroups.Add(parentTraverseGroup.Name); - resultToken.ensuredTraverseGroups.Add(parentTraverseGroup.Name); - DefaultLogger.LogEntry(LogLevels.Debug, $"Vorhandene Traverse-Gruppe per Wildcard wiederverwendet: {parentTraverseGroup.Name}"); - } - } - if (parentTraverseGroup == null && !string.IsNullOrWhiteSpace(traverseNameTemplate)) { for (var loop = 0; loop < 20; loop++) @@ -809,6 +797,13 @@ namespace C4IT_IAM_SET } } + if (parentTraverseGroup == null && hasTraverseWildcard && !forceStrictAdGroupNames) + { + DefaultLogger.LogEntry( + LogLevels.Debug, + $"Traverse wildcard '{traverseRegex}' is only used for ACL-linked groups on '{parent.FullName}'. Global wildcard reuse is skipped to avoid reusing a traverse group from another path."); + } + if (parentTraverseGroup == null && !string.IsNullOrWhiteSpace(traverseNameTemplate)) { DefaultLogger.LogEntry(LogLevels.Debug, "Erstelle neue TraverseGroup."); @@ -952,6 +947,11 @@ namespace C4IT_IAM_SET } if (currentSecGroup.Scope != GroupScope.Global) continue; + if (IsSameGroupName(currentSecGroup.Name, displayTraverseGroupName)) + { + DefaultLogger.LogEntry(LogLevels.Debug, $"Skip self membership for Traverse-Gruppe '{displayTraverseGroupName}'."); + continue; + } if (WhatIf) { @@ -972,7 +972,11 @@ namespace C4IT_IAM_SET { try { - if (WhatIf) + if (IsSameGroupName(displayTraverseGroupName, traverseGroupName)) + { + DefaultLogger.LogEntry(LogLevels.Debug, $"Skip nested self membership for Traverse-Gruppe '{displayTraverseGroupName}'."); + } + else if (WhatIf) { resultToken.warnings.Add($"Traverse-Gruppe '{displayTraverseGroupName}' würde verschachtelte Gruppe '{traverseGroupName}' erhalten."); } @@ -1055,14 +1059,45 @@ namespace C4IT_IAM_SET && PathsEqual(boundaryPath, path); } - private Dictionary GetTraverseReplacementTags(string currentPath) + private Dictionary GetTraverseReplacementTags(string currentPath, GroupScope traverseScope) { var visibleSegments = GetVisibleTraversePathSegments(currentPath); - return new Dictionary(StringComparer.OrdinalIgnoreCase) + var tags = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (groupCustomTags != null) { - { "TRAVERSE_NAME", Helper.SanitizePathSegment(GetLastPathSegment(currentPath), groupNameSanitizeReplacement) }, - { "TRAVERSE_VISIBLEPATH", Helper.JoinSanitizedPathSegments(visibleSegments.Select(i => Helper.SanitizePathSegment(i, groupNameSanitizeReplacement)), groupNameSanitizeReplacement) } - }; + foreach (var customTag in groupCustomTags) + tags[customTag.Key] = customTag.Value; + } + + tags["PREFIX"] = groupPrefix; + tags["GROUPTYPEPOSTFIX"] = groupTraverseTag; + tags["SCOPETAG"] = traverseScope == GroupScope.Local ? groupDLTag : groupGTag; + tags["TRAVERSE_NAME"] = Helper.SanitizePathSegment(GetLastPathSegment(currentPath), groupNameSanitizeReplacement); + tags["TRAVERSE_VISIBLEPATH"] = Helper.JoinSanitizedPathSegments(visibleSegments.Select(i => Helper.SanitizePathSegment(i, groupNameSanitizeReplacement)), groupNameSanitizeReplacement); + return tags; + } + + private static bool IsSameGroupName(string left, string right) + { + return !string.IsNullOrWhiteSpace(left) + && !string.IsNullOrWhiteSpace(right) + && string.Equals(left, right, StringComparison.OrdinalIgnoreCase); + } + + private static bool IsSamePrincipal(GroupPrincipal left, GroupPrincipal right) + { + if (left == null || right == null) + return false; + + var leftSid = left.Sid?.Value; + var rightSid = right.Sid?.Value; + if (!string.IsNullOrWhiteSpace(leftSid) && !string.IsNullOrWhiteSpace(rightSid)) + return string.Equals(leftSid, rightSid, StringComparison.OrdinalIgnoreCase); + + if (!string.IsNullOrWhiteSpace(left.DistinguishedName) && !string.IsNullOrWhiteSpace(right.DistinguishedName)) + return string.Equals(left.DistinguishedName, right.DistinguishedName, StringComparison.OrdinalIgnoreCase); + + return IsSameGroupName(left.SamAccountName ?? left.Name, right.SamAccountName ?? right.Name); } private IEnumerable GetVisibleTraversePathSegments(string currentPath) @@ -1245,6 +1280,12 @@ namespace C4IT_IAM_SET if (parentTraverseGroup == null || traverseGroup == null) return false; + if (IsSamePrincipal(parentTraverseGroup, traverseGroup)) + { + DefaultLogger.LogEntry(LogLevels.Debug, $"Skip nested self membership for Traverse-Gruppe '{parentTraverseGroup.Name}'."); + return true; + } + return RetryTraverseMembershipAction( traverseGroup.Name, true, @@ -1554,11 +1595,11 @@ namespace C4IT_IAM_SET DirectoryInfo dInfo = new DirectoryInfo(technicalName); //DirectoryInfo dInfoBaseFolder = new DirectoryInfo(baseFolderTechnicalName); - // Get a DirectorySecurity object that represents the + // Get a DirectorySecurity object that represents the // current security settings. DirectorySecurity dSecurity = dInfo.GetAccessControl(); - // Add the FileSystemAccessRule to the security settings. + // Add the FileSystemAccessRule to the security settings. var everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null); dSecurity.AddAccessRule(new FileSystemAccessRule(everyone,