diff --git a/Features/ADCompareDetailsProvider.cs b/Features/ADCompareDetailsProvider.cs new file mode 100644 index 0000000..97ba743 --- /dev/null +++ b/Features/ADCompareDetailsProvider.cs @@ -0,0 +1,129 @@ +using Disco.Data.Repository; +using Disco.Models.Repository; +using Disco.Services.Interop.ActiveDirectory; +using Disco.Services.Plugins; +using Disco.Services.Plugins.Features.DetailsProvider; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Disco.Plugins.ADCompare.Features +{ + [PluginFeature(Id = "ADCompareDetails", Name = "AD Compare Detail Provider", PrimaryFeature = true)] + public class ADCompareDetailsProvider : DetailsProviderFeature + { + private const string DetailsScope = "Details"; + private const string ADCompareScope = "ADCompare"; + + /// + /// Updates all user details by comparing AD attributes against Disco records. + /// Stores comparison metadata as UserDetails with ADCompare scope. + /// + public override void UpdateAllDetails(DiscoDataContext database) + { + var users = database.Users.ToList(); + + foreach (var user in users) + { + try + { + var adUser = ActiveDirectory.RetrieveADUserAccount(user.UserId); + if (adUser == null) + { + SetUserDetail(database, user, ADCompareScope, "ADStatus", "NotFound"); + SetUserDetail(database, user, ADCompareScope, "LastChecked", DateTime.Now.ToString("o")); + continue; + } + + // Store comparison results as UserDetails + SetUserDetail(database, user, ADCompareScope, "ADStatus", adUser.IsDisabled ? "Disabled" : "Active"); + SetUserDetail(database, user, ADCompareScope, "LastChecked", DateTime.Now.ToString("o")); + + // Store AD values for reference + SetUserDetail(database, user, ADCompareScope, "AD_DisplayName", adUser.DisplayName ?? string.Empty); + SetUserDetail(database, user, ADCompareScope, "AD_Surname", adUser.Surname ?? string.Empty); + SetUserDetail(database, user, ADCompareScope, "AD_GivenName", adUser.GivenName ?? string.Empty); + SetUserDetail(database, user, ADCompareScope, "AD_Email", adUser.Email ?? string.Empty); + SetUserDetail(database, user, ADCompareScope, "AD_Phone", adUser.Phone ?? string.Empty); + SetUserDetail(database, user, ADCompareScope, "AD_DistinguishedName", adUser.DistinguishedName ?? string.Empty); + + // Check for mismatches + var mismatches = new List(); + if (!StringMatch(user.DisplayName, adUser.DisplayName)) mismatches.Add("DisplayName"); + if (!StringMatch(user.Surname, adUser.Surname)) mismatches.Add("Surname"); + if (!StringMatch(user.GivenName, adUser.GivenName)) mismatches.Add("GivenName"); + if (!StringMatch(user.EmailAddress, adUser.Email)) mismatches.Add("Email"); + if (!StringMatch(user.PhoneNumber, adUser.Phone)) mismatches.Add("Phone"); + + SetUserDetail(database, user, ADCompareScope, "MismatchedFields", + mismatches.Count > 0 ? string.Join(",", mismatches) : "None"); + } + catch (Exception ex) + { + SetUserDetail(database, user, ADCompareScope, "ADStatus", "Error"); + SetUserDetail(database, user, ADCompareScope, "ADError", ex.Message); + SetUserDetail(database, user, ADCompareScope, "LastChecked", DateTime.Now.ToString("o")); + } + } + + database.SaveChanges(); + } + + /// + /// Gets user photo from AD (thumbnailPhoto attribute). + /// Returns null if no photo or cache is still valid. + /// + public override byte[] GetUserPhoto(DiscoDataContext database, User user, DateTime? cacheTimestamp) + { + // Only refresh photos older than 24 hours + if (cacheTimestamp.HasValue && cacheTimestamp.Value > DateTime.Now.AddHours(-24)) + return null; + + try + { + var adUser = ActiveDirectory.RetrieveADUserAccount(user.UserId, new[] { "thumbnailPhoto" }); + if (adUser == null) + return null; + + var photoData = adUser.GetPropertyValue("thumbnailPhoto"); + return photoData; + } + catch + { + return null; + } + } + + #region Helpers + + private bool StringMatch(string discoValue, string adValue) + { + var a = string.IsNullOrWhiteSpace(discoValue) ? string.Empty : discoValue.Trim(); + var b = string.IsNullOrWhiteSpace(adValue) ? string.Empty : adValue.Trim(); + return string.Equals(a, b, StringComparison.OrdinalIgnoreCase); + } + + private void SetUserDetail(DiscoDataContext database, User user, string scope, string key, string value) + { + var existing = database.UserDetails + .FirstOrDefault(d => d.UserId == user.UserId && d.Scope == scope && d.Key == key); + + if (existing != null) + { + existing.Value = value; + } + else + { + database.UserDetails.Add(new UserDetail + { + UserId = user.UserId, + Scope = scope, + Key = key, + Value = value + }); + } + } + + #endregion + } +}