From 802f6902553971c74fc4f946f8509d8622f7342a Mon Sep 17 00:00:00 2001 From: jessikitty Date: Mon, 27 Apr 2026 10:17:19 +1000 Subject: [PATCH] Add Device page UIExtension - shows AD managedBy comparison and last login --- Features/DeviceUIExtension.cs | 188 ++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 Features/DeviceUIExtension.cs diff --git a/Features/DeviceUIExtension.cs b/Features/DeviceUIExtension.cs new file mode 100644 index 0000000..5aa8758 --- /dev/null +++ b/Features/DeviceUIExtension.cs @@ -0,0 +1,188 @@ +using Disco.Models.UI.Device; +using Disco.Services.Interop.ActiveDirectory; +using Disco.Services.Plugins; +using Disco.Services.Plugins.Features.UIExtension; +using System; +using System.Text; +using System.Web.Mvc; + +namespace Disco.Plugins.ADCompare.Features +{ + [PluginFeature(Id = "ADCompareDeviceUI", Name = "Device Page - AD Compare")] + public class DeviceUIExtension : UIExtensionFeature + { + public override UIExtensionResult ExecuteAction(ControllerContext context, DeviceShowModel model) + { + var device = model.Device; + if (device == null || string.IsNullOrEmpty(device.DeviceDomainId)) + return Nothing(); + + var html = new StringBuilder(); + html.Append("
"); + html.Append("

"); + html.Append(" AD Compare

"); + + try + { + var adAccount = ActiveDirectory.RetrieveADMachineAccount( + device.DeviceDomainId, + new[] { "managedBy", "lastLogonTimestamp" }); + + if (adAccount == null) + { + html.Append("
"); + html.Append(" Computer not found in Active Directory"); + html.Append("
"); + } + else + { + var managedByDN = adAccount.GetPropertyValue("managedBy"); + var discoAssignedUser = device.AssignedUserId; + var hasManagedBy = !string.IsNullOrEmpty(managedByDN); + var hasAssignment = !string.IsNullOrEmpty(discoAssignedUser); + + string adManagedByUserId = null; + string adManagedByName = null; + + if (hasManagedBy) + { + try + { + var managedByUser = ActiveDirectory.RetrieveADUserAccount(managedByDN); + if (managedByUser != null) + { + adManagedByUserId = managedByUser.Id; + adManagedByName = managedByUser.DisplayName; + } + else + { + adManagedByUserId = managedByDN; + } + } + catch + { + adManagedByUserId = managedByDN; + } + } + + bool isMatch = false; + if (!hasAssignment && !hasManagedBy) + isMatch = true; + else if (hasAssignment && hasManagedBy) + isMatch = string.Equals(discoAssignedUser, adManagedByUserId, StringComparison.OrdinalIgnoreCase); + + html.Append(""); + + if (isMatch) + { + html.Append(""); + } + else + { + html.Append(""); + } + + html.Append(""); + + html.Append(""); + + var lastLogonTicks = adAccount.GetPropertyValue("lastLogonTimestamp"); + html.Append(""); + + if (adAccount.IsDisabled) + { + html.Append(""); + } + + html.Append("
"); + html.Append(" Managed By matches Assigned User"); + html.Append("
"); + html.Append(" Managed By does NOT match Assigned User"); + html.Append("
Disco Assigned User"); + if (hasAssignment) + { + html.Append(Encode(discoAssignedUser)); + if (device.AssignedUser != null) + { + html.Append(" ("); + html.Append(Encode(device.AssignedUser.DisplayName)); + html.Append(")"); + } + } + else + { + html.Append("Not assigned"); + } + html.Append("
AD Managed By"); + if (hasManagedBy) + { + html.Append(Encode(adManagedByUserId)); + if (!string.IsNullOrEmpty(adManagedByName)) + { + html.Append(" ("); + html.Append(Encode(adManagedByName)); + html.Append(")"); + } + } + else + { + html.Append("Empty"); + } + html.Append("
Last Login"); + if (lastLogonTicks > 0) + { + try + { + var lastLogon = DateTime.FromFileTime(lastLogonTicks); + var ago = DateTime.Now.Subtract(lastLogon); + html.Append(Encode(lastLogon.ToString("dd/MM/yyyy hh:mm tt"))); + html.Append(" ("); + html.Append(FormatTimeAgo(ago)); + html.Append(")"); + } + catch + { + html.Append("Unable to parse"); + } + } + else + { + html.Append("Never"); + } + html.Append("
AD Status"); + html.Append(" Disabled"); + html.Append("
"); + } + } + catch (Exception ex) + { + html.Append("
"); + html.Append(" AD lookup error: "); + html.Append(Encode(ex.Message)); + html.Append("
"); + } + + html.Append("
"); + return Literal(html.ToString()); + } + + private string Encode(string value) + { + if (string.IsNullOrEmpty(value)) return ""; + return System.Web.HttpUtility.HtmlEncode(value); + } + + private string FormatTimeAgo(TimeSpan span) + { + if (span.TotalDays >= 365) + return string.Format("{0} year(s) ago", (int)(span.TotalDays / 365)); + if (span.TotalDays >= 30) + return string.Format("{0} month(s) ago", (int)(span.TotalDays / 30)); + if (span.TotalDays >= 1) + return string.Format("{0} day(s) ago", (int)span.TotalDays); + if (span.TotalHours >= 1) + return string.Format("{0} hour(s) ago", (int)span.TotalHours); + return "Just now"; + } + } +}