4e01cbc2c3
Diagnostic for pinning down the LDAP settings the four ZB.MOM.WW apps need against the Zimmer NAM domain, and for answering which AD groups can be mapped to app roles. Found the referral defect fixed in 0.2.1. Two independent halves in one run: Part A drives the REAL ZB.MOM.WW.Auth.LdapAuthService -- same bind-then-search- then-bind, same options validator, same failure enum -- so its verdict IS what the apps produce rather than an approximation of it. Part B asks the directory what the library cannot, which is where the group answer comes from: full group DNs, nested groups via LDAP_MATCHING_RULE_IN_CHAIN, the primary group (AD omits it from memberOf, so it can never be a role group), security vs distribution type, decoded userAccountControl, referral counts, and the TLS chain with key sizes. --list-groups '<pat>' enumerates every matching group in the directory, which a membership dump structurally cannot do -- it only ever shows one account's memberships. Passwords are prompted for and never echoed; they cannot be passed as arguments, which would put them in shell history. --self-bind lets the user under test stand in for the service account, so group discovery does not require the svc_igntnapp secret. Not packable, outside the Auth solution and its central package management, referencing the Auth projects directly so it tracks current source.
86 lines
2.8 KiB
C#
86 lines
2.8 KiB
C#
namespace ZB.MOM.WW.Tools.LdapProbe;
|
|
|
|
/// <summary>Console formatting and credential prompting.</summary>
|
|
public static class Ui
|
|
{
|
|
public static void Section(string title)
|
|
{
|
|
Console.WriteLine();
|
|
Write(ConsoleColor.Cyan, $"── {title} " + new string('─', Math.Max(0, 66 - title.Length)));
|
|
}
|
|
|
|
public static void Ok(string message) => Write(ConsoleColor.Green, " ✓ " + message);
|
|
public static void Bad(string message) => Write(ConsoleColor.Red, " ✗ " + message);
|
|
public static void Warn(string message) => Write(ConsoleColor.Yellow, " ! " + message);
|
|
public static void Info(string message) => Console.WriteLine(" " + message);
|
|
|
|
public static void Field(string name, string? value) =>
|
|
Console.WriteLine($" {name,-22} {value ?? "(not set)"}");
|
|
|
|
private static void Write(ConsoleColor color, string message)
|
|
{
|
|
var previous = Console.ForegroundColor;
|
|
try
|
|
{
|
|
Console.ForegroundColor = color;
|
|
Console.WriteLine(message);
|
|
}
|
|
finally
|
|
{
|
|
Console.ForegroundColor = previous;
|
|
}
|
|
}
|
|
|
|
public static string Prompt(string label, string? preset = null)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(preset)) return preset;
|
|
Console.Write($" {label}: ");
|
|
return Console.ReadLine()?.Trim() ?? "";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads a password without echoing it. Falls back to a plain read when input is
|
|
/// redirected (a pipe has no key events), so the probe still works non-interactively.
|
|
/// </summary>
|
|
public static string PromptSecret(string label)
|
|
{
|
|
Console.Write($" {label}: ");
|
|
|
|
if (Console.IsInputRedirected)
|
|
{
|
|
var piped = Console.ReadLine() ?? "";
|
|
Console.WriteLine("(read from stdin)");
|
|
return piped;
|
|
}
|
|
|
|
var buffer = new Stack<char>();
|
|
while (true)
|
|
{
|
|
var key = Console.ReadKey(intercept: true);
|
|
switch (key.Key)
|
|
{
|
|
case ConsoleKey.Enter:
|
|
Console.WriteLine();
|
|
return new string(buffer.Reverse().ToArray());
|
|
case ConsoleKey.Backspace when buffer.Count > 0:
|
|
buffer.Pop();
|
|
Console.Write("\b \b");
|
|
break;
|
|
case ConsoleKey.Backspace:
|
|
break;
|
|
case ConsoleKey.Escape:
|
|
Console.WriteLine();
|
|
return "";
|
|
default:
|
|
if (!char.IsControl(key.KeyChar))
|
|
{
|
|
buffer.Push(key.KeyChar);
|
|
Console.Write('*');
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|