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
@@ -2,6 +2,7 @@ using System.Collections.Concurrent;
using System.DirectoryServices;
using SGU.AuthBroker.Core.Directory;
using SGU.AuthBroker.Core.Identity;
using SGU.AuthBroker.Core.Profiles;
using SGU.AuthBroker.Options;
namespace SGU.AuthBroker.Services;
@@ -19,6 +20,7 @@ public sealed class ActiveDirectorySynchronizer(BrokerOptions options) : IActive
public async Task<DirectorySyncResult> SynchronizeAsync(
UserIdentity identity,
InstitutionalProfile? profile,
string password,
CancellationToken cancellationToken)
{
@@ -27,7 +29,7 @@ public sealed class ActiveDirectorySynchronizer(BrokerOptions options) : IActive
try
{
return await Task.Run(
() => Synchronize(identity, password),
() => Synchronize(identity, profile, password),
cancellationToken).ConfigureAwait(false);
}
finally
@@ -40,7 +42,10 @@ public sealed class ActiveDirectorySynchronizer(BrokerOptions options) : IActive
}
}
private DirectorySyncResult Synchronize(UserIdentity identity, string password)
private DirectorySyncResult Synchronize(
UserIdentity identity,
InstitutionalProfile? profile,
string password)
{
string targetOuDn = options.GetOuDn(identity.Role);
using DirectoryEntry root = Bind(options.BaseDn);
@@ -95,6 +100,8 @@ public sealed class ActiveDirectorySynchronizer(BrokerOptions options) : IActive
user.Properties["pwdLastSet"].Value = -1;
user.CommitChanges();
TryApplyProfile(user, identity, profile);
return new DirectorySyncResult(
options.DomainNetbios,
identity.UserName,
@@ -108,6 +115,54 @@ public sealed class ActiveDirectorySynchronizer(BrokerOptions options) : IActive
}
}
private static void TryApplyProfile(
DirectoryEntry user,
UserIdentity identity,
InstitutionalProfile? profile)
{
if (profile is null)
{
return;
}
try
{
SetOptionalProperty(user, "displayName", profile.DisplayName);
SetOptionalProperty(user, "mail", profile.Email);
SetOptionalProperty(user, "title", profile.JobTitle);
SetOptionalProperty(user, "department", profile.Department);
SetOptionalProperty(user, "employeeType", profile.EmployeeType);
if (string.Equals(profile.EmployeeNumber, identity.NumericId, StringComparison.Ordinal))
{
SetOptionalProperty(user, "employeeID", profile.EmployeeNumber);
}
user.CommitChanges();
}
catch
{
// Metadata is intentionally best-effort. User creation, password sync,
// and account enablement have already committed successfully.
try
{
user.RefreshCache();
}
catch
{
// Discarding the optional property cache must not alter the
// already committed password synchronization result.
}
}
}
private static void SetOptionalProperty(DirectoryEntry entry, string propertyName, string? value)
{
if (!string.IsNullOrWhiteSpace(value))
{
entry.Properties[propertyName].Value = value;
}
}
private DirectoryEntry BindOrCreateOu(string ouDn, DirectoryEntry root)
{
try