Bound NTFS AD group name lengths
This commit is contained in:
@@ -530,13 +530,25 @@ namespace C4IT_IAM_SET
|
||||
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);
|
||||
var boundedTraverseContext = Helper.GetBoundedAdGroupTemplateContext(
|
||||
traverseGroupTemplate.NamingTemplate,
|
||||
true,
|
||||
relativePath,
|
||||
sanitizedSegments,
|
||||
folderName,
|
||||
null,
|
||||
Helper.MaxAdGroupNameLength,
|
||||
$"Traverse fuer '{parent.FullName}'");
|
||||
var adjustedTraverseSegments = boundedTraverseContext.SanitizedSegments ?? Array.Empty<string>();
|
||||
var adjustedTraverseRelativePath = adjustedTraverseSegments.Length > 0 ? string.Join("_", adjustedTraverseSegments) : string.Empty;
|
||||
var adjustedTraverseFolderName = boundedTraverseContext.FolderName;
|
||||
var traverseNameTemplate = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.NamingTemplate, true, adjustedTraverseRelativePath, adjustedTraverseSegments, adjustedTraverseFolderName);
|
||||
var traverseDescriptionTemplate = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.DescriptionTemplate, true, adjustedTraverseRelativePath, adjustedTraverseSegments, adjustedTraverseFolderName);
|
||||
|
||||
string traverseRegex = null;
|
||||
try
|
||||
{
|
||||
traverseRegex = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.WildcardTemplate, true, relativePath, sanitizedSegments, folderName);
|
||||
traverseRegex = Helper.ApplyTemplatePlaceholders(traverseGroupTemplate.WildcardTemplate, true, adjustedTraverseRelativePath, adjustedTraverseSegments, adjustedTraverseFolderName);
|
||||
DefaultLogger.LogEntry(LogLevels.Debug, $"traverseRegex: {traverseRegex}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using C4IT.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -10,57 +11,141 @@ namespace C4IT_IAM_Engine
|
||||
{
|
||||
public static class Helper
|
||||
{
|
||||
public const int MaxAdGroupNameLength = 64;
|
||||
public const int MaxAdGroupLoopDigits = 3;
|
||||
|
||||
public sealed class BoundedTemplateContext
|
||||
{
|
||||
public string[] SanitizedSegments { get; set; } = Array.Empty<string>();
|
||||
public string FolderName { get; set; } = string.Empty;
|
||||
public bool WasShortened { get; set; }
|
||||
public string OriginalValue { get; set; } = string.Empty;
|
||||
public string FinalValue { get; set; } = string.Empty;
|
||||
public string Strategy { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public static string ReplaceLoopTag(this string str, int loop)
|
||||
{
|
||||
return Regex.Replace(str, @"(?<loopTag>{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}})", loop <= 0 ? "" : "${prefix}" + loop + "${postfix}");
|
||||
}
|
||||
public static string ReplaceTags(this string str, IDictionary<string, string> dict)
|
||||
{
|
||||
if (str.Equals(string.Empty) || str == null || dict == null || dict.Count == 0)
|
||||
return str;
|
||||
return dict.Aggregate(str, (current, value) =>
|
||||
current.Replace("{{" + value.Key + "}}", value.Value));
|
||||
}
|
||||
public static string ApplyTemplatePlaceholders(string templateValue, bool allowRelativePath, string defaultRelativePath, string[] sanitizedSegments, string folderName)
|
||||
{
|
||||
if (templateValue == null)
|
||||
return string.Empty;
|
||||
|
||||
var result = Regex.Replace(templateValue, @"{{\s*NAME\s*}}", folderName ?? string.Empty, RegexOptions.IgnoreCase);
|
||||
|
||||
if (allowRelativePath)
|
||||
{
|
||||
result = Regex.Replace(result, @"{{\s*RELATIVEPATH(?:\s*\(\s*(\d+)\s*\))?\s*}}", match =>
|
||||
{
|
||||
if (sanitizedSegments == null || sanitizedSegments.Length == 0)
|
||||
return string.Empty;
|
||||
|
||||
if (!match.Groups[1].Success)
|
||||
return defaultRelativePath;
|
||||
|
||||
if (!int.TryParse(match.Groups[1].Value, out var segmentIndex) || segmentIndex < 0)
|
||||
return defaultRelativePath;
|
||||
|
||||
var segmentCount = Math.Min(sanitizedSegments.Length, segmentIndex + 1);
|
||||
var skip = sanitizedSegments.Length - segmentCount;
|
||||
return string.Join("_", sanitizedSegments.Skip(skip));
|
||||
}, RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
public static string SanitizePathSegment(string segment)
|
||||
{
|
||||
if (string.IsNullOrEmpty(segment))
|
||||
return string.Empty;
|
||||
|
||||
return Regex.Replace(segment, @"[\s\-]", "_");
|
||||
}
|
||||
public static void CreatePathWithWriteAccess(string FilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var PF = Environment.ExpandEnvironmentVariables(FilePath);
|
||||
public static string ReplaceTags(this string str, IDictionary<string, string> dict)
|
||||
{
|
||||
if (str.Equals(string.Empty) || str == null || dict == null || dict.Count == 0)
|
||||
return str;
|
||||
return dict.Aggregate(str, (current, value) =>
|
||||
current.Replace("{{" + value.Key + "}}", value.Value));
|
||||
}
|
||||
public static string ApplyTemplatePlaceholders(string templateValue, bool allowRelativePath, string defaultRelativePath, string[] sanitizedSegments, string folderName)
|
||||
{
|
||||
if (templateValue == null)
|
||||
return string.Empty;
|
||||
|
||||
var result = Regex.Replace(templateValue, @"{{\s*NAME\s*}}", folderName ?? string.Empty, RegexOptions.IgnoreCase);
|
||||
|
||||
if (allowRelativePath)
|
||||
{
|
||||
result = Regex.Replace(result, @"{{\s*RELATIVEPATH(?:\s*\(\s*(\d+)\s*\))?\s*}}", match =>
|
||||
{
|
||||
if (sanitizedSegments == null || sanitizedSegments.Length == 0)
|
||||
return string.Empty;
|
||||
|
||||
if (!match.Groups[1].Success)
|
||||
return defaultRelativePath;
|
||||
|
||||
if (!int.TryParse(match.Groups[1].Value, out var segmentIndex) || segmentIndex < 0)
|
||||
return defaultRelativePath;
|
||||
|
||||
var segmentCount = Math.Min(sanitizedSegments.Length, segmentIndex + 1);
|
||||
var skip = sanitizedSegments.Length - segmentCount;
|
||||
return string.Join("_", sanitizedSegments.Skip(skip));
|
||||
}, RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
public static BoundedTemplateContext GetBoundedAdGroupTemplateContext(
|
||||
string templateValue,
|
||||
bool allowRelativePath,
|
||||
string defaultRelativePath,
|
||||
string[] sanitizedSegments,
|
||||
string folderName,
|
||||
IDictionary<string, string> replacementTags,
|
||||
int maxLength,
|
||||
string logContext)
|
||||
{
|
||||
var effectiveSegments = (sanitizedSegments ?? Array.Empty<string>()).Where(i => i != null).ToArray();
|
||||
var effectiveFolderName = folderName ?? string.Empty;
|
||||
var availableLength = Math.Max(1, maxLength - GetLoopReservationLength(templateValue));
|
||||
var originalValue = MaterializeTemplateValue(templateValue, allowRelativePath, defaultRelativePath, effectiveSegments, effectiveFolderName, replacementTags);
|
||||
var usesRelativePath = allowRelativePath && Regex.IsMatch(templateValue ?? string.Empty, @"{{\s*RELATIVEPATH", RegexOptions.IgnoreCase);
|
||||
var usesName = Regex.IsMatch(templateValue ?? string.Empty, @"{{\s*NAME\s*}}", RegexOptions.IgnoreCase);
|
||||
var strategy = string.Empty;
|
||||
|
||||
while (originalValue.Length > availableLength)
|
||||
{
|
||||
var changed = false;
|
||||
|
||||
if (usesRelativePath && TryShortenRelativePath(ref effectiveSegments))
|
||||
{
|
||||
effectiveFolderName = effectiveSegments.Length > 0 ? effectiveSegments[effectiveSegments.Length - 1] : string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(strategy))
|
||||
strategy = "truncate-relativepath";
|
||||
changed = true;
|
||||
}
|
||||
else if (usesName && !usesRelativePath && TryShortenName(ref effectiveFolderName))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(strategy))
|
||||
strategy = "truncate-name";
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!changed)
|
||||
break;
|
||||
|
||||
originalValue = MaterializeTemplateValue(templateValue, allowRelativePath, defaultRelativePath, effectiveSegments, effectiveFolderName, replacementTags);
|
||||
}
|
||||
|
||||
var result = new BoundedTemplateContext
|
||||
{
|
||||
SanitizedSegments = effectiveSegments,
|
||||
FolderName = effectiveSegments.Length > 0 ? effectiveSegments[effectiveSegments.Length - 1] : effectiveFolderName,
|
||||
OriginalValue = MaterializeTemplateValue(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName, replacementTags),
|
||||
FinalValue = originalValue,
|
||||
WasShortened = !string.Equals(
|
||||
MaterializeTemplateValue(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName, replacementTags),
|
||||
originalValue,
|
||||
StringComparison.Ordinal),
|
||||
Strategy = strategy
|
||||
};
|
||||
|
||||
if (result.WasShortened)
|
||||
{
|
||||
cLogManager.DefaultLogger.LogEntry(
|
||||
LogLevels.Warning,
|
||||
$"AD-Gruppenname gekuerzt ({logContext}): '{result.OriginalValue}' ({result.OriginalValue.Length}) -> '{result.FinalValue}' ({result.FinalValue.Length}), Strategie: {result.Strategy}, Limit: {availableLength}.");
|
||||
}
|
||||
|
||||
if (result.FinalValue.Length > availableLength)
|
||||
{
|
||||
cLogManager.DefaultLogger.LogEntry(
|
||||
LogLevels.Warning,
|
||||
$"AD-Gruppenname ueberschreitet weiterhin das sichere Limit ({logContext}): '{result.FinalValue}' ({result.FinalValue.Length}), Limit: {availableLength}.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
public static string SanitizePathSegment(string segment)
|
||||
{
|
||||
if (string.IsNullOrEmpty(segment))
|
||||
return string.Empty;
|
||||
|
||||
return Regex.Replace(segment, @"[\s\-]", "_");
|
||||
}
|
||||
public static void CreatePathWithWriteAccess(string FilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var PF = Environment.ExpandEnvironmentVariables(FilePath);
|
||||
Directory.CreateDirectory(PF);
|
||||
}
|
||||
catch { }
|
||||
@@ -77,5 +162,80 @@ namespace C4IT_IAM_Engine
|
||||
else
|
||||
return new string(maskingChar, input.Length);
|
||||
}
|
||||
|
||||
private static string MaterializeTemplateValue(
|
||||
string templateValue,
|
||||
bool allowRelativePath,
|
||||
string defaultRelativePath,
|
||||
string[] sanitizedSegments,
|
||||
string folderName,
|
||||
IDictionary<string, string> replacementTags)
|
||||
{
|
||||
return ApplyTemplatePlaceholders(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName)
|
||||
.ReplaceTags(replacementTags)
|
||||
.ToUpper();
|
||||
}
|
||||
|
||||
private static int GetLoopReservationLength(string templateValue)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(templateValue))
|
||||
return 0;
|
||||
|
||||
var reservation = 0;
|
||||
foreach (Match match in Regex.Matches(templateValue, @"{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}}", RegexOptions.IgnoreCase))
|
||||
{
|
||||
reservation += match.Groups["prefix"].Value.Length + MaxAdGroupLoopDigits + match.Groups["postfix"].Value.Length;
|
||||
}
|
||||
|
||||
return reservation;
|
||||
}
|
||||
|
||||
private static bool TryShortenRelativePath(ref string[] segments)
|
||||
{
|
||||
if (segments == null || segments.Length == 0)
|
||||
return false;
|
||||
|
||||
var lastIndex = segments.Length - 1;
|
||||
var candidateIndex = -1;
|
||||
var candidateLength = 1;
|
||||
|
||||
for (var i = 0; i < lastIndex; i++)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(segments[i]) || segments[i].Length <= candidateLength)
|
||||
continue;
|
||||
|
||||
candidateIndex = i;
|
||||
candidateLength = segments[i].Length;
|
||||
}
|
||||
|
||||
if (candidateIndex >= 0)
|
||||
{
|
||||
segments[candidateIndex] = segments[candidateIndex].Substring(0, segments[candidateIndex].Length - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (segments.Length > 1)
|
||||
{
|
||||
segments = segments.Skip(1).ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (segments[lastIndex].Length > 1)
|
||||
{
|
||||
segments[lastIndex] = segments[lastIndex].Substring(0, segments[lastIndex].Length - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryShortenName(ref string folderName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(folderName) || folderName.Length <= 1)
|
||||
return false;
|
||||
|
||||
folderName = folderName.Substring(0, folderName.Length - 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,16 +189,39 @@ 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)
|
||||
var replacementTags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
if (customTags != null)
|
||||
{
|
||||
foreach (var customTag in customTags)
|
||||
replacementTags[customTag.Key] = customTag.Value;
|
||||
}
|
||||
foreach (var tag in tags)
|
||||
replacementTags[tag.Key] = tag.Value;
|
||||
|
||||
var boundedNameContext = Helper.GetBoundedAdGroupTemplateContext(
|
||||
template.NamingTemplate,
|
||||
template.Type != SecurityGroupType.Traverse,
|
||||
relativePath,
|
||||
sanitizedSegments,
|
||||
folderName,
|
||||
replacementTags,
|
||||
Helper.MaxAdGroupNameLength,
|
||||
$"{template.Type}/{template.Scope} fuer '{newFolderPath}'");
|
||||
|
||||
var adjustedSegments = boundedNameContext.SanitizedSegments ?? Array.Empty<string>();
|
||||
var adjustedRelativePath = adjustedSegments.Length > 0 ? string.Join("_", adjustedSegments) : string.Empty;
|
||||
var adjustedFolderName = boundedNameContext.FolderName;
|
||||
|
||||
template.NamingTemplate = Helper.ApplyTemplatePlaceholders(template.NamingTemplate, template.Type != SecurityGroupType.Traverse, adjustedRelativePath, adjustedSegments, adjustedFolderName)
|
||||
.ReplaceTags(customTags).ReplaceTags(tags)
|
||||
.ToUpper();
|
||||
|
||||
template.DescriptionTemplate = Helper.ApplyTemplatePlaceholders(template.DescriptionTemplate, template.Type != SecurityGroupType.Traverse, relativePath, sanitizedSegments, folderName)
|
||||
template.DescriptionTemplate = Helper.ApplyTemplatePlaceholders(template.DescriptionTemplate, template.Type != SecurityGroupType.Traverse, adjustedRelativePath, adjustedSegments, adjustedFolderName)
|
||||
.ReplaceTags(customTags).ReplaceTags(tags)
|
||||
.ToUpper();
|
||||
|
||||
|
||||
template.WildcardTemplate = Helper.ApplyTemplatePlaceholders(template.WildcardTemplate, template.Type != SecurityGroupType.Traverse, relativePath, sanitizedSegments, folderName)
|
||||
template.WildcardTemplate = Helper.ApplyTemplatePlaceholders(template.WildcardTemplate, template.Type != SecurityGroupType.Traverse, adjustedRelativePath, adjustedSegments, adjustedFolderName)
|
||||
.ReplaceTags(customTags).ReplaceTags(tags)
|
||||
.ToUpper();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user