Files

201 lines
9.6 KiB
C#

using Disco.Plugins.ADCompare.Features;
using Disco.Services.Plugins;
using System;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
namespace Disco.Plugins.ADCompare.WebHandler
{
public class ADCompareWebHandler : PluginWebHandler
{
public override ActionResult ExecuteAction(string ActionName)
{
var action = ActionName != null ? ActionName.ToLower() : "";
switch (action)
{
case "":
case "index":
return Index();
case "run":
return Run();
case "export":
return ExportCsv();
default:
return new HttpNotFoundResult();
}
}
private ActionResult Index()
{
return HtmlResult(BuildPage(null));
}
private ActionResult Run()
{
var service = new DeviceCompareService(Database);
var summary = service.CompareAllDevices();
return HtmlResult(BuildPage(summary));
}
private ActionResult ExportCsv()
{
var service = new DeviceCompareService(Database);
var summary = service.CompareAllDevices();
var sb = new StringBuilder();
sb.AppendLine("SerialNumber,ComputerName,DiscoAssignedUser,DiscoAssignedUserName,ADManagedBy,Match,Reason");
foreach (var r in summary.Results.Where(r => !r.IsMatch))
{
sb.AppendLine(string.Join(",",
CsvEsc(r.SerialNumber), CsvEsc(r.ComputerName),
CsvEsc(r.DiscoAssignedUserId), CsvEsc(r.DiscoAssignedUserDisplayName),
CsvEsc(r.ADManagedByDisplayName),
r.IsMatch.ToString(), CsvEsc(r.MismatchReason)));
}
var fileName = "AD_ManagedBy_Compare_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".csv";
HostController.Response.Headers.Add("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
return new ContentResult
{
Content = sb.ToString(),
ContentType = "text/csv",
ContentEncoding = Encoding.UTF8
};
}
private ActionResult HtmlResult(string html)
{
return new ContentResult
{
Content = html,
ContentType = "text/html",
ContentEncoding = Encoding.UTF8
};
}
private string CsvEsc(string v)
{
if (v == null) v = "";
return "\"" + v.Replace("\"", "\"\"") + "\"";
}
private string H(string v)
{
if (string.IsNullOrEmpty(v)) return "";
return HttpUtility.HtmlEncode(v);
}
private string BuildPage(Models.DeviceComparisonSummary summary)
{
var pluginUrl = "/Plugin/Disco.Plugins.ADCompare";
var sb = new StringBuilder();
sb.Append("<!DOCTYPE html><html><head><meta charset='utf-8'/>");
sb.Append("<title>AD Compare - Device Managed By</title>");
sb.Append("<style>");
sb.Append("body{font-family:Segoe UI,Arial,sans-serif;margin:20px;background:#fff;color:#333;}");
sb.Append("h2{color:#333;border-bottom:2px solid #337ab7;padding-bottom:10px;}");
sb.Append(".btn{display:inline-block;padding:8px 16px;font-size:14px;border:none;border-radius:4px;cursor:pointer;text-decoration:none;color:#fff;margin-right:8px;}");
sb.Append(".btn-primary{background:#337ab7;} .btn-primary:hover{background:#286090;}");
sb.Append(".btn-default{background:#777;} .btn-default:hover{background:#555;}");
sb.Append(".summary{display:flex;gap:20px;margin:15px 0;padding:15px;background:#f5f5f5;border-radius:4px;flex-wrap:wrap;}");
sb.Append(".stat{text-align:center;min-width:100px;} .stat .num{font-size:28px;font-weight:bold;} .stat .lbl{font-size:12px;color:#666;}");
sb.Append(".green{color:#5cb85c;} .orange{color:#f0ad4e;} .red{color:#d9534f;} .blue{color:#5bc0de;} .grey{color:#999;}");
sb.Append("table{width:100%;border-collapse:collapse;margin-top:10px;} th{background:#f5f5f5;text-align:left;padding:8px;border-bottom:2px solid #ddd;}");
sb.Append("td{padding:8px;border-bottom:1px solid #eee;} tr:hover{background:#f9f9f9;}");
sb.Append(".warn-row{background:#fcf8e3;}");
sb.Append(".badge{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;color:#fff;}");
sb.Append(".badge-warn{background:#f0ad4e;}");
sb.Append(".muted{color:#999;font-style:italic;} .small{font-size:12px;color:#888;}");
sb.Append(".info-bar{margin:10px 0;padding:8px 12px;background:#d9edf7;border-radius:4px;font-size:13px;color:#31708f;}");
sb.Append("</style></head><body>");
sb.Append("<h2>AD Compare &mdash; Device Managed By</h2>");
sb.Append("<p>Compares the AD computer <strong>Managed By</strong> field against the Disco <strong>Assigned User</strong> for domain-joined devices found in AD.</p>");
sb.Append("<div style='margin:15px 0;'>");
sb.Append("<a href='" + pluginUrl + "/Run' class='btn btn-primary'>&#x21bb; Run Comparison</a>");
if (summary != null)
{
sb.Append("<a href='" + pluginUrl + "/Export' class='btn btn-default'>&#x2B07; Export Mismatches CSV</a>");
}
sb.Append("</div>");
if (summary != null)
{
// Summary stats - only devices found in AD
sb.Append("<div class='summary'>");
sb.Append("<div class='stat'><div class='num'>" + summary.TotalDevices + "</div><div class='lbl'>Devices Checked</div></div>");
sb.Append("<div class='stat'><div class='num green'>" + summary.DevicesMatched + "</div><div class='lbl'>Matched</div></div>");
sb.Append("<div class='stat'><div class='num orange'>" + summary.DevicesMismatched + "</div><div class='lbl'>Mismatched</div></div>");
sb.Append("<div class='stat'><div class='num blue'>" + summary.DevicesNoManagedBy + "</div><div class='lbl'>No ManagedBy</div></div>");
sb.Append("<div class='stat'><div class='num grey'>" + summary.DevicesNoAssignment + "</div><div class='lbl'>No Assignment</div></div>");
sb.Append("</div>");
// Info bar for filtered items
if (summary.DevicesNotInAD > 0 || summary.SkippedDevices > 0)
{
sb.Append("<div class='info-bar'>");
sb.Append("<strong>Filtered out:</strong> ");
if (summary.DevicesNotInAD > 0)
{
sb.Append(summary.DevicesNotInAD + " device(s) not found in AD");
}
if (summary.DevicesNotInAD > 0 && summary.SkippedDevices > 0)
{
sb.Append(", ");
}
if (summary.SkippedDevices > 0)
{
sb.Append(summary.SkippedDevices + " device(s) without valid domain ID");
}
sb.Append("</div>");
}
// Results table - mismatches only (all results are AD-found devices)
var mismatches = summary.Results.Where(r => !r.IsMatch).ToList();
if (mismatches.Count == 0)
{
sb.Append("<div style='padding:20px;background:#dff0d8;border-radius:4px;margin-top:15px;'>");
sb.Append("<strong>&#x2713; All devices match!</strong> Every device found in AD has its Managed By matching the Disco assigned user.");
sb.Append("</div>");
}
else
{
sb.Append("<h3 style='margin-top:20px;'>Mismatches (" + mismatches.Count + ")</h3>");
sb.Append("<table><thead><tr>");
sb.Append("<th>Serial Number</th><th>Computer Name</th>");
sb.Append("<th>Disco Assigned User</th><th>AD Managed By</th><th>Reason</th>");
sb.Append("</tr></thead><tbody>");
foreach (var r in mismatches)
{
string discoUser = !string.IsNullOrEmpty(r.DiscoAssignedUserId)
? H(r.DiscoAssignedUserId) + (r.DiscoAssignedUserDisplayName != null
? "<br/><span class='small'>" + H(r.DiscoAssignedUserDisplayName) + "</span>" : "")
: "<span class='muted'>Not assigned</span>";
string adUser = !string.IsNullOrEmpty(r.ADManagedByDisplayName)
? H(r.ADManagedByDisplayName)
: "<span class='muted'>Empty</span>";
sb.Append("<tr class='warn-row'>");
sb.Append("<td>" + H(r.SerialNumber) + "</td>");
sb.Append("<td>" + H(r.ComputerName) + "</td>");
sb.Append("<td>" + discoUser + "</td>");
sb.Append("<td>" + adUser + "</td>");
sb.Append("<td>" + H(r.MismatchReason) + "</td>");
sb.Append("</tr>");
}
sb.Append("</tbody></table>");
}
}
sb.Append("</body></html>");
return sb.ToString();
}
}
}