Initial checkin

This commit is contained in:
2023-11-06 10:41:40 -05:00
parent 3f1aac52da
commit 9b8ae7d33b
7 changed files with 52882 additions and 0 deletions

25
LDIFTools.sln Executable file
View File

@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.6.33723.286
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LDIFTools", "LDIFTools\LDIFTools.csproj", "{4ED13164-5D94-4ACE-9435-58060DBC836A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4ED13164-5D94-4ACE-9435-58060DBC836A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4ED13164-5D94-4ACE-9435-58060DBC836A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4ED13164-5D94-4ACE-9435-58060DBC836A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4ED13164-5D94-4ACE-9435-58060DBC836A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {17B5541E-1C33-46BA-AB49-672B4361E444}
EndGlobalSection
EndGlobal

16
LDIFTools/LDIFTools.csproj Executable file
View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Update="input.ldif">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

516
LDIFTools/Program.cs Executable file
View File

@@ -0,0 +1,516 @@
using System.Net.NetworkInformation;
using System.Text;
namespace LDIFTools
{
internal class Program
{
static string? InputFilename = null;
static string? OutputFilename = null;
static List<string> FileStart = new();
static SortedList<string, List<string>> SortedDNs = new();
static List<List<string>> NotSortedDNs = new();
static bool SortDNs = false;
static bool RemoveComments = false;
static List<string> KeepStrings = new();
static List<string> RemoveStrings = new();
static bool UseLongLines = false;
static List<string> AttrsToRemove = new();
static string DefaultAttrsToRemove = "showInAdvancedViewOnly,objectGUID,instanceType,dsCorePropagationData,distinguishedName,whenCreated,whenChanged,uSNCreated,uSNChanged,name,objectCategory";
static bool SortVISConfigItems = false;
static bool UppercaseVISConfigItems = false;
static int Main(string[] args)
{
Console.WriteLine("LDIFTools\n");
//Gather the processing options from the command line
int parse = ParseArguments(args);
if (parse > 0)
{
PrintUsage();
return parse;
}
//Print out a summary of the processing options
Console.WriteLine($"Processing: {InputFilename} --> {OutputFilename}");
Console.WriteLine($" DNs will be sorted: {SortDNs}");
Console.WriteLine($" Commented lines will be removed: {RemoveComments}");
Console.WriteLine($" Remove line breaks from long lines: {UseLongLines}");
Console.WriteLine($" Sort vis-configurationItems: {SortVISConfigItems}");
Console.WriteLine($" Capitalize name portion of vis-configurationItems: {UppercaseVISConfigItems}");
if (AttrsToRemove.Count > 0)
{
Console.WriteLine($" Remove attributes: {string.Join("\n ",AttrsToRemove.ToArray())}");
}
else
{
Console.WriteLine(" Remove attributes: None");
}
if (KeepStrings.Count > 0)
{
foreach (string keep in KeepStrings)
{
Console.WriteLine($" Keep: {keep}");
}
}
else if (RemoveStrings.Count > 0)
{
foreach (string remove in RemoveStrings)
{
Console.WriteLine($" Remove: {remove}");
}
}
else
{
Console.WriteLine(" No filtering via -k or -r will be done");
}
Console.WriteLine();
//Verify we should overwrite the output file if it exists
if (File.Exists(OutputFilename))
{
Console.WriteLine($"Output file already exists: {OutputFilename}");
Console.Write("Overwrite (y/N)? ");
ConsoleKeyInfo key = Console.ReadKey();
Console.WriteLine("\n");
if (key.KeyChar.ToString().ToLower() != "y")
{
Console.WriteLine("No changes made");
return 0;
}
}
ReadInputFile(InputFilename!);
WriteOutputFile(OutputFilename!);
return 0;
}
static int ParseArguments(string[] args)
{
int argCounter = 0;
while (argCounter < args.Length)
{
if (args[argCounter] == "-i")
{
if (argCounter + 1 < args.Length && !string.IsNullOrEmpty(args[argCounter + 1]))
{
InputFilename = args[argCounter + 1].Trim();
if (File.Exists(InputFilename))
{
argCounter += 2;
}
else
{
Console.WriteLine($"File does not exist: {InputFilename}");
return 1;
}
}
else
{
Console.WriteLine("No input file");
return 1;
}
}
else if (args[argCounter] == "-o")
{
if (argCounter + 1 < args.Length && !string.IsNullOrEmpty(args[argCounter + 1]))
{
OutputFilename = args[argCounter + 1].Trim();
argCounter += 2;
}
else
{
Console.WriteLine("No output file");
return 1;
}
}
else if (args[argCounter] == "-s")
{
SortDNs = true;
argCounter++;
}
else if (args[argCounter] == "-c")
{
RemoveComments = true;
argCounter++;
}
else if (args[argCounter] == "-l")
{
UseLongLines = true;
argCounter++;
}
else if (args[argCounter] == "-v")
{
UseLongLines = true;
SortVISConfigItems = true;
argCounter++;
}
else if (args[argCounter] == "-vc")
{
UseLongLines = true;
SortVISConfigItems = true;
UppercaseVISConfigItems = true;
argCounter++;
}
else if (args[argCounter] == "-x")
{
UseLongLines = true;
if (argCounter + 1 < args.Length && !string.IsNullOrEmpty(args[argCounter + 1]))
{
AttrsToRemove.AddRange(args[argCounter + 1].Split(',', StringSplitOptions.TrimEntries));
argCounter += 2;
}
else
{
Console.WriteLine("Wrong usage of the -x option");
return 1;
}
}
else if (args[argCounter] == "-xd")
{
UseLongLines = true;
AttrsToRemove.AddRange(DefaultAttrsToRemove.Split(','));
argCounter++;
}
else if (args[argCounter] == "-k")
{
UseLongLines = true;
if (argCounter + 1 < args.Length && !string.IsNullOrEmpty(args[argCounter + 1]))
{
KeepStrings.Add(args[argCounter + 1]);
argCounter += 2;
}
else
{
Console.WriteLine("Wrong usage of the -k option");
return 1;
}
}
else if (args[argCounter] == "-r")
{
UseLongLines = true;
if (argCounter + 1 < args.Length && !string.IsNullOrEmpty(args[argCounter + 1]))
{
RemoveStrings.Add(args[argCounter + 1]);
argCounter += 2;
}
else
{
Console.WriteLine("Wrong usage of the -r option");
return 1;
}
}
else
{
Console.WriteLine($"Unknown option: {args[argCounter]}");
return 1;
}
}
if (InputFilename == null || OutputFilename == null)
{
return 1;
}
FileInfo InputFileInfo = new(InputFilename);
if (File.Exists(OutputFilename))
{
FileInfo OutputFileInfo = new(OutputFilename);
if (InputFileInfo.FullName.ToLower().Equals(OutputFileInfo.FullName.ToLower()))
{
Console.WriteLine("Input file cannot be the same as the output file");
return 1;
}
}
else if (InputFileInfo.Name.ToLower() == Path.GetFileName(OutputFilename).ToLower())
{
Console.WriteLine("Input file cannot be the same as the output file");
return 1;
}
if (KeepStrings.Count > 0 && RemoveStrings.Count > 0)
{
Console.WriteLine("The -k and -r options are exclusive. Pick one or the other.");
return 1;
}
return 0;
}
static void PrintUsage()
{
Console.WriteLine();
Console.WriteLine("Usage: LDIFTools -i <inputfile.ldif> -o <outputfile.ldif>");
Console.WriteLine();
Console.WriteLine(" Required options:");
Console.WriteLine(" -i name of LDIF file to process");
Console.WriteLine(" -o name of output file - cannot be the same as the input file");
Console.WriteLine();
Console.WriteLine(" Done before any filtering:");
Console.WriteLine(" -c remove any commented lines found below the first DN in the file");
Console.WriteLine(" -l remove line breaks from long lines");
Console.WriteLine(" -x <text> remove attributes from all DNs - a comma-separated list");
Console.WriteLine(" -xd remove the following attributes from all DNs:");
Console.WriteLine(" showInAdvancedViewOnly,objectGUID,instanceType,dsCorePropagationData,");
Console.WriteLine(" whenCreated,whenChanged,uSNCreated,uSNChanged,");
Console.WriteLine(" distinguishedName,name,objectCategory");
Console.WriteLine();
Console.WriteLine(" Filtering (optional): choose either -k or -r, but not both.");
Console.WriteLine(" Multiple -k options keeps DNs that match any -k option.");
Console.WriteLine(" Multiple -r options removes DNs that match any -r option.");
Console.WriteLine(" -k <text> keep DNs that contain this text somewhere in the definition");
Console.WriteLine(" -r <text> remove any DNs that contain this text in the definition");
Console.WriteLine();
Console.WriteLine(" Done after any filtering:");
Console.WriteLine(" -s sort the DNs in the output (you probably want -c if you do this)");
Console.WriteLine(" -v sort the vis-configurationItem attributes in the DNs");
Console.WriteLine(" -vc sort the vis-configurationItem attributes in the DNs and also");
Console.WriteLine(" uppercase the name portion (before the first ^)");
Console.WriteLine();
Console.WriteLine(" All line-based options imply -l is turned on also: -k, -r, -x, -xd, -v, -vc");
Console.WriteLine();
Console.WriteLine("Example:");
Console.WriteLine(" LDIFTools -i ofis5.ldif -o just_tenant_stuff.ldif -c -s -xd -vc");
Console.WriteLine(" -k CN=__TENANTS,CN=__GLOBAL -k OU=TENANTS,OU=CloudLDAP");
}
static void ReadInputFile(string inputFilename)
{
bool startOfFile = true;
List<string> currentBlock = new();
string currentDN = "";
foreach (string line in File.ReadLines(inputFilename))
{
if (line.ToLower().StartsWith("dn:") && line.Trim().Length > 4)
{
//We have a new dn block to read
//Save current block if we've been building one
if (!startOfFile)
{
if (SortDNs)
SortedDNs.Add(currentDN, FilterAttrs(currentBlock));
else
NotSortedDNs.Add(FilterAttrs(currentBlock));
}
//We're past the start of the file now
startOfFile = false;
//Start building a new block of lines
currentDN = ReverseDN(line.Trim().Replace("dn:", "").Trim().ToLower());
currentBlock = new()
{
line
};
}
else if (startOfFile)
{
FileStart.Add(line);
}
else
{
if (!(RemoveComments && line.Trim().StartsWith("#")))
{
if (UseLongLines && line.StartsWith(" "))
{
//append line (without starting space) to last line read from file
currentBlock[^1] += line[1..];
}
else
{
currentBlock.Add(line);
}
}
}
}
if (!startOfFile)
{
if (SortDNs)
SortedDNs.Add(currentDN, FilterAttrs(currentBlock));
else
NotSortedDNs.Add(FilterAttrs(currentBlock));
}
}
static List<string> FilterAttrs(List<string> dnBlock)
{
List<string> result = new();
foreach (string line in dnBlock)
{
bool save = true;
foreach (string attr in AttrsToRemove)
{
if (line.ToLower().StartsWith(attr.ToLower() + ":"))
{
save = false;
break;
}
}
if (save)
{
result.Add(line);
}
}
return result;
}
static List<string> CleanupVISConfigItems(List<string> dnBlock)
{
List<string> result = new();
SortedList<string, string> configItems = new();
int startsAt = -1;
if (!SortVISConfigItems) return dnBlock;
int counter = 0;
foreach (string line in dnBlock)
{
if (line.ToLower().StartsWith("vis-configurationitem:"))
{
if (startsAt == -1) startsAt = counter;
string lineToClean;
bool base64 = false;
if (line.ToLower().StartsWith("vis-configurationitem::"))
{
//Base64 encoded attribute
base64 = true;
var base64EncodedBytes = Convert.FromBase64String(line.Replace("vis-configurationitem::", "", StringComparison.InvariantCultureIgnoreCase).Trim());
lineToClean = Encoding.UTF8.GetString(base64EncodedBytes);
}
else
lineToClean = line.Replace("vis-configurationitem:", "", StringComparison.InvariantCultureIgnoreCase);
if (UppercaseVISConfigItems)
{
string[] values = lineToClean.Split('^');
if (values.Length > 0)
{
values[0] = values[0].ToUpper().Trim();
}
string newAttrValue = string.Join("^", values);
string cleaned;
if (base64)
{
var textBytes = Encoding.UTF8.GetBytes(newAttrValue);
var base64String = Convert.ToBase64String(textBytes);
cleaned = "vis-configurationItem:: " + base64String;
}
else
cleaned = "vis-configurationItem: " + newAttrValue;
configItems.Add(lineToClean.ToLower(), cleaned);
}
else
{
configItems.Add(lineToClean.ToLower(), line);
}
}
else
{
result.Add(line);
}
counter++;
}
if (configItems.Count > 0)
{
result.InsertRange(startsAt, configItems.Values);
}
return result;
}
static bool ShouldKeepBlock(List<string> dnBlock)
{
//Should we keep this block?
bool save = true;
if (KeepStrings.Count > 0)
{
bool keep = false;
foreach (string dnLine in dnBlock)
{
foreach (string keepString in KeepStrings)
{
if (dnLine.ToLower().Contains(keepString.ToLower()))
{
keep = true;
break;
}
}
}
save = keep;
}
else if (RemoveStrings.Count > 0)
{
bool remove = false;
foreach (string dnLine in dnBlock)
{
foreach (string removeString in RemoveStrings)
{
if (dnLine.ToLower().Contains(removeString.ToLower()))
{
remove = true;
break;
}
}
}
save = !remove;
}
return save;
}
static void WriteOutputFile(string outputFilename)
{
if (File.Exists(outputFilename))
{
Console.WriteLine("Removing existing output file...");
File.Delete(outputFilename);
Console.WriteLine("Done");
Console.WriteLine();
}
Console.WriteLine("Writing new output file...");
File.AppendAllLines(outputFilename, FileStart);
if (SortDNs)
{
foreach (List<string> dn in SortedDNs.Values)
{
if (ShouldKeepBlock(dn))
{
File.AppendAllLines(outputFilename, CleanupVISConfigItems(dn));
}
}
}
else
{
foreach (List<string> dn in NotSortedDNs)
{
if (ShouldKeepBlock(dn))
{
File.AppendAllLines(outputFilename, CleanupVISConfigItems(dn));
}
}
}
Console.WriteLine("Done");
Console.WriteLine();
}
static string ReverseDN(string dn)
{
string[]? dnParts = dn.Split(',');
if (dnParts.Length == 0)
{
return dn;
}
return string.Join(',', dnParts.Reverse().ToArray());
}
}
}

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net7.0\publish\osx-arm64\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>osx-arm64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net7.0\publish\win-x64\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<PublishReadyToRun>true</PublishReadyToRun>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,8 @@
{
"profiles": {
"LDIFTools": {
"commandName": "Project",
"commandLineArgs": "-s -xd -vc -i input.ldif -c -o output.ldif -k CN=__TENANTS,CN=__GLOBAL -k OU=TENANTS,OU=CloudLDAP"
}
}
}

52280
LDIFTools/input.ldif Executable file

File diff suppressed because it is too large Load Diff