Enrich AD users from SGU profile metadata

This commit is contained in:
2026-09-01 07:34:00 -06:00
parent 289a67e371
commit 3d0897316d
16 changed files with 563 additions and 22 deletions
@@ -0,0 +1,180 @@
using System.Net;
using System.Net.Mail;
using System.Text;
namespace SGU.AuthBroker.Core.Profiles;
public static class SguProfileParser
{
private const string AdministrativeNameId = "ctl00_contenedor_decEncabezado_lblNombre";
private const string EmployeeTypeId = "ctl00_contenedor_decEncabezado_lblIndicadorValue";
private const string EmailId = "ctl00_contenedor_decEncabezado_lblCorreo";
private const string JobTitleId = "ctl00_contenedor_decEncabezado_lblPuesto";
private const string DepartmentId = "ctl00_contenedor_decEncabezado_lblDependencia";
private const string MenuNameId = "ctl00_lblNombreUsuario";
public static InstitutionalProfile? ParseAdministrative(string html, string expectedEmployeeNumber)
{
ArgumentNullException.ThrowIfNull(html);
ArgumentException.ThrowIfNullOrWhiteSpace(expectedEmployeeNumber);
string? identity = ExtractSpanText(html, AdministrativeNameId);
if (!TrySplitAdministrativeIdentity(identity, out string? employeeNumber, out string? displayName) ||
!string.Equals(employeeNumber, expectedEmployeeNumber, StringComparison.Ordinal))
{
return null;
}
InstitutionalProfile profile = new(
EmployeeNumber: employeeNumber,
DisplayName: Limit(displayName, 256),
Email: NormalizeEmail(ExtractSpanText(html, EmailId)),
EmployeeType: Limit(ExtractSpanText(html, EmployeeTypeId), 256),
JobTitle: Limit(ExtractSpanText(html, JobTitleId), 64),
Department: Limit(ExtractSpanText(html, DepartmentId), 64));
return profile.HasValues ? profile : null;
}
public static InstitutionalProfile? ParseMenu(string html)
{
ArgumentNullException.ThrowIfNull(html);
InstitutionalProfile profile = new(
DisplayName: Limit(ExtractSpanText(html, MenuNameId), 256));
return profile.HasValues ? profile : null;
}
private static bool TrySplitAdministrativeIdentity(
string? value,
out string? employeeNumber,
out string? displayName)
{
employeeNumber = null;
displayName = null;
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
int separator = value.IndexOf(" - ", StringComparison.Ordinal);
if (separator != 6)
{
return false;
}
string candidateNumber = value[..separator];
string candidateName = value[(separator + 3)..].Trim();
if (candidateNumber.Length != 6 ||
!candidateNumber.All(char.IsAsciiDigit) ||
string.IsNullOrWhiteSpace(candidateName))
{
return false;
}
employeeNumber = candidateNumber;
displayName = candidateName;
return true;
}
private static string? ExtractSpanText(string html, string id)
{
foreach (char quote in new[] { '"', '\'' })
{
string marker = $"id={quote}{id}{quote}";
int searchFrom = 0;
while (searchFrom < html.Length)
{
int idIndex = html.IndexOf(marker, searchFrom, StringComparison.OrdinalIgnoreCase);
if (idIndex < 0)
{
break;
}
int spanStart = html.LastIndexOf("<span", idIndex, StringComparison.OrdinalIgnoreCase);
int precedingTagEnd = html.LastIndexOf('>', idIndex);
if (spanStart > precedingTagEnd)
{
int openingTagEnd = html.IndexOf('>', idIndex);
int closingTagStart = openingTagEnd < 0
? -1
: html.IndexOf("</span", openingTagEnd + 1, StringComparison.OrdinalIgnoreCase);
if (openingTagEnd >= 0 && closingTagStart >= 0)
{
string innerHtml = html[(openingTagEnd + 1)..closingTagStart];
return NormalizeText(innerHtml);
}
}
searchFrom = idIndex + marker.Length;
}
}
return null;
}
private static string? NormalizeText(string htmlFragment)
{
StringBuilder withoutTags = new(htmlFragment.Length);
bool insideTag = false;
foreach (char character in htmlFragment)
{
if (character == '<')
{
insideTag = true;
}
else if (character == '>')
{
insideTag = false;
}
else if (!insideTag)
{
withoutTags.Append(character);
}
}
string decoded = WebUtility.HtmlDecode(withoutTags.ToString());
StringBuilder normalized = new(decoded.Length);
bool previousWasWhitespace = true;
foreach (char character in decoded)
{
bool whitespace = char.IsWhiteSpace(character) || character == '\u00A0';
if (whitespace)
{
if (!previousWasWhitespace)
{
normalized.Append(' ');
}
}
else
{
normalized.Append(character);
}
previousWasWhitespace = whitespace;
}
string result = normalized.ToString().Trim();
return result.Length == 0 ? null : result;
}
private static string? NormalizeEmail(string? value)
{
string? candidate = Limit(value, 256);
if (candidate is null ||
!MailAddress.TryCreate(candidate, out MailAddress? address) ||
!string.Equals(address.Address, candidate, StringComparison.OrdinalIgnoreCase))
{
return null;
}
return address.Address;
}
private static string? Limit(string? value, int maximumLength)
{
string? candidate = value?.Trim();
return string.IsNullOrEmpty(candidate) || candidate.Length > maximumLength
? null
: candidate;
}
}