#107 allow moving of device OU when changing profile

This commit is contained in:
Gary Sharp
2024-01-12 12:53:50 +11:00
parent dcc4fcb984
commit 8afe4195a9
7 changed files with 447 additions and 299 deletions
@@ -3,6 +3,7 @@ using Disco.Models.Repository;
using Disco.Models.Services.Devices.Exporting; using Disco.Models.Services.Devices.Exporting;
using Disco.Models.Services.Devices.Importing; using Disco.Models.Services.Devices.Importing;
using Disco.Models.Services.Documents; using Disco.Models.Services.Documents;
using System.Data.Entity;
using Disco.Services; using Disco.Services;
using Disco.Services.Authorization; using Disco.Services.Authorization;
using Disco.Services.Devices.Exporting; using Disco.Services.Devices.Exporting;
@@ -22,6 +23,7 @@ using System.Linq;
using System.Web; using System.Web;
using System.Web.Caching; using System.Web.Caching;
using System.Web.Mvc; using System.Web.Mvc;
using Disco.Services.Logging;
namespace Disco.Web.Areas.API.Controllers namespace Disco.Web.Areas.API.Controllers
{ {
@@ -113,10 +115,54 @@ namespace Disco.Web.Areas.API.Controllers
#region Update Shortcut Methods #region Update Shortcut Methods
[DiscoAuthorize(Claims.Device.Properties.DeviceProfile)] [DiscoAuthorize(Claims.Device.Properties.DeviceProfile), HttpPost, ValidateAntiForgeryToken]
public virtual ActionResult UpdateDeviceProfileId(string id, string DeviceProfileId = null, bool redirect = false) public virtual ActionResult UpdateDeviceProfileId(string id, string DeviceProfileId = null, bool enforceOrganisationalUnit = false, bool redirect = false)
{ {
return Update(id, pDeviceProfileId, DeviceProfileId, redirect); var updateResult = Update(id, pDeviceProfileId, DeviceProfileId, redirect);
if (enforceOrganisationalUnit)
{
var device = Database.Devices
.Include(d => d.DeviceProfile)
.First(d => d.SerialNumber == id);
if (ActiveDirectory.IsValidDomainAccountId(device.DeviceDomainId, out ADDomain deviceDomain))
{
var ou = device.DeviceProfile.OrganisationalUnit;
if (string.IsNullOrWhiteSpace(ou))
ou = ActiveDirectory.Context.PrimaryDomain.DefaultComputerContainer;
var domain = ActiveDirectory.Context.GetDomainFromDistinguishedName(ou);
if (domain != deviceDomain)
SystemLog.LogWarning($"Device '{device.SerialNumber}' [{device.DeviceDomainId}] is not in the same domain as the Organisational Unit '{ou}' and cannot be moved");
else
{
var domainController = domain.GetAvailableDomainController(RequireWritable: true);
var deviceAccount = domainController.RetrieveADMachineAccount(device.DeviceDomainId);
if (deviceAccount == null)
SystemLog.LogWarning($"Device '{device.SerialNumber}' [{device.DeviceDomainId}] was not found on the domain controller");
else
{
if (!string.Equals(deviceAccount.ParentDistinguishedName, ou, StringComparison.OrdinalIgnoreCase))
{
try
{
var existingOu = deviceAccount.ParentDistinguishedName;
deviceAccount.MoveOrganisationalUnit(domainController, ou);
SystemLog.LogInformation($"Device Profile Updated; Moved Device '{device.SerialNumber}' [{device.DeviceDomainId}] from '{existingOu}' to '{ou}'");
}
catch (Exception ex)
{
SystemLog.LogException($"Failed to move Device '{device.SerialNumber}' [{device.DeviceDomainId}] from '{deviceAccount.ParentDistinguishedName}' to '{ou}'", ex);
}
}
}
}
}
}
return updateResult;
} }
[DiscoAuthorize(Claims.Device.Properties.DeviceBatch)] [DiscoAuthorize(Claims.Device.Properties.DeviceBatch)]
+14 -9
View File
@@ -199,23 +199,28 @@
border-top: none; border-top: none;
background-color: #eee; background-color: #eee;
} }
#Device_Show_Policies_Profile_Actions_Update_Dialog ul li, #Device_Show_Policies_Profile_Actions_Update_Dialog .profile-list,
#Device_Show_Policies_Batch_Actions_Update_Dialog ul li { #Device_Show_Policies_Batch_Actions_Update_Dialog .profile-list {
max-height: 300px;
overflow-y: auto;
}
#Device_Show_Policies_Profile_Actions_Update_Dialog .profile-list li,
#Device_Show_Policies_Batch_Actions_Update_Dialog .profile-list li {
background-color: #fff; background-color: #fff;
padding: 2px 0 2px 4px; padding: 2px 0 2px 4px;
} }
#Device_Show_Policies_Profile_Actions_Update_Dialog ul li:nth-child(odd), #Device_Show_Policies_Profile_Actions_Update_Dialog .profile-list li:nth-child(odd),
#Device_Show_Policies_Batch_Actions_Update_Dialog ul li:nth-child(odd) { #Device_Show_Policies_Batch_Actions_Update_Dialog .profile-list li:nth-child(odd) {
background-color: hsl(0, 0%, 98.5%); background-color: hsl(0, 0%, 98.5%);
} }
#Device_Show_Policies_Profile_Actions_Update_Dialog ul li.selected, #Device_Show_Policies_Profile_Actions_Update_Dialog .profile-list li.selected,
#Device_Show_Policies_Batch_Actions_Update_Dialog ul li.selected { #Device_Show_Policies_Batch_Actions_Update_Dialog .profile-list li.selected {
background-color: #cddbec; background-color: #cddbec;
font-weight: 600; font-weight: 600;
} }
#Device_Show_Policies_Profile_Actions_Update_Dialog label, #Device_Show_Policies_Profile_Actions_Update_Dialog .enforce-ou,
#Device_Show_Policies_Batch_Actions_Update_Dialog label { #Device_Show_Policies_Batch_Actions_Update_Dialog .enforce-ou {
display: block; padding-top: 0.5em;
} }
#DeviceDetailTab-JobsContainer div.jobTable { #DeviceDetailTab-JobsContainer div.jobTable {
margin: -1px; margin: -1px;
+9 -6
View File
@@ -170,7 +170,10 @@
} }
#Device_Show_Policies_Profile_Actions_Update_Dialog, #Device_Show_Policies_Batch_Actions_Update_Dialog { #Device_Show_Policies_Profile_Actions_Update_Dialog, #Device_Show_Policies_Batch_Actions_Update_Dialog {
ul { .profile-list {
max-height: 300px;
overflow-y: auto;
li { li {
background-color: @white; background-color: @white;
padding: 2px 0 2px 4px; padding: 2px 0 2px 4px;
@@ -185,8 +188,8 @@
} }
} }
} }
label { .enforce-ou {
display: block; padding-top: .5em;
} }
} }
@@ -619,9 +622,9 @@
background-color: @TableDataRowBackgroundColor; background-color: @TableDataRowBackgroundColor;
} }
td:nth-child(n+3) { td:nth-child(n+3) {
color: @SubtleBorderColour; color: @SubtleBorderColour;
} }
} }
tr.actionModified { tr.actionModified {
File diff suppressed because one or more lines are too long
@@ -320,6 +320,7 @@ namespace Disco.Web.Areas.API.Controllers
{ {
public readonly string id = "id"; public readonly string id = "id";
public readonly string DeviceProfileId = "DeviceProfileId"; public readonly string DeviceProfileId = "DeviceProfileId";
public readonly string enforceOrganisationalUnit = "enforceOrganisationalUnit";
public readonly string redirect = "redirect"; public readonly string redirect = "redirect";
} }
static readonly ActionParamsClass_UpdateDeviceBatchId s_params_UpdateDeviceBatchId = new ActionParamsClass_UpdateDeviceBatchId(); static readonly ActionParamsClass_UpdateDeviceBatchId s_params_UpdateDeviceBatchId = new ActionParamsClass_UpdateDeviceBatchId();
@@ -582,16 +583,17 @@ namespace Disco.Web.Areas.API.Controllers
} }
[NonAction] [NonAction]
partial void UpdateDeviceProfileIdOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string id, string DeviceProfileId, bool redirect); partial void UpdateDeviceProfileIdOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string id, string DeviceProfileId, bool enforceOrganisationalUnit, bool redirect);
[NonAction] [NonAction]
public override System.Web.Mvc.ActionResult UpdateDeviceProfileId(string id, string DeviceProfileId, bool redirect) public override System.Web.Mvc.ActionResult UpdateDeviceProfileId(string id, string DeviceProfileId, bool enforceOrganisationalUnit, bool redirect)
{ {
var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.UpdateDeviceProfileId); var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.UpdateDeviceProfileId);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "id", id); ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "id", id);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "DeviceProfileId", DeviceProfileId); ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "DeviceProfileId", DeviceProfileId);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "enforceOrganisationalUnit", enforceOrganisationalUnit);
ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "redirect", redirect); ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "redirect", redirect);
UpdateDeviceProfileIdOverride(callInfo, id, DeviceProfileId, redirect); UpdateDeviceProfileIdOverride(callInfo, id, DeviceProfileId, enforceOrganisationalUnit, redirect);
return callInfo; return callInfo;
} }
@@ -386,31 +386,38 @@
</table> </table>
@if (Model.Device.CanUpdateDeviceProfile()) @if (Model.Device.CanUpdateDeviceProfile())
{ {
@Html.ActionLinkSmallButton("Update Profile", MVC.API.Device.UpdateDeviceProfileId(Model.Device.SerialNumber, redirect: true), "Device_Show_Policies_Profile_Actions_Update_Button") <button id="Device_Show_Policies_Profile_Actions_Update_Button" class="button small">Update Profile</button>
<div id="Device_Show_Policies_Profile_Actions_Update_Dialog" class="dialog" title="Assign to Device Profile"> <div id="Device_Show_Policies_Profile_Actions_Update_Dialog" class="dialog" title="Assign to Device Profile">
<div> @using (Html.BeginForm(MVC.API.Device.UpdateDeviceProfileId(Model.Device.SerialNumber, redirect: true)))
<ul class="none"> {
@foreach (var dp in Model.DeviceProfiles.OrderBy(i => i.Name)) <div class="profile-list">
{ @Html.AntiForgeryToken()
var isDecommissioned = Model.DecommissionedDeviceProfileIds.Contains(dp.Id); <ul class="none">
<li class="@(isDecommissioned ? "hidden" : null)"> @foreach (var dp in Model.DeviceProfiles.OrderBy(i => i.Name))
<label title="Distribution: @(dp.DistributionType)">
<input type="radio" data-deviceprofileid="@dp.Id" name="DeviceProfile" />
@dp.Name
</label>
</li>
if (isDecommissioned)
{ {
<li class="hidden decommissioned-padding"></li> var isDecommissioned = Model.DecommissionedDeviceProfileIds.Contains(dp.Id);
<li class="@(isDecommissioned ? "hidden" : null)">
<label title="Distribution: @(dp.DistributionType)">
<input type="radio" name="DeviceProfileId" value="@dp.Id" data-ouenforced="@dp.EnforceOrganisationalUnit" @(Model.Device.DeviceProfileId == dp.Id ? "checked " : null) />
@dp.Name
</label>
</li>
if (isDecommissioned)
{
<li class="hidden decommissioned-padding"></li>
}
} }
</ul>
@if (Model.DecommissionedDeviceProfileIds.Count > 0)
{
<a class="button small show-decommissioned" href="#">Show Decommissioned</a>
} }
</ul> </div>
@if (Model.DecommissionedDeviceProfileIds.Count > 0) <div class="enforce-ou">
{ <input id="deviceProfileMoveOrganisationalUnit" type="checkbox" name="enforceOrganisationalUnit" value="true" /><label for="deviceProfileMoveOrganisationalUnit">Move to Profiles Organisational Unit</label>
<a class="button small show-decommissioned" href="#">Show Decommissioned</a> </div>
} }
</div>
</div> </div>
<script> <script>
$(function () { $(function () {
@@ -420,7 +427,8 @@
var dialogInputs = null; var dialogInputs = null;
var dialogContainers = null; var dialogContainers = null;
button.click(function () { button.click(function (e) {
e.preventDefault();
if (!buttonDialog) { if (!buttonDialog) {
buttonDialog = $('#Device_Show_Policies_Profile_Actions_Update_Dialog') buttonDialog = $('#Device_Show_Policies_Profile_Actions_Update_Dialog')
@@ -431,13 +439,12 @@
autoOpen: false, autoOpen: false,
buttons: { buttons: {
"Update Profile": function () { "Update Profile": function () {
var deviceProfileId = dialogInputs.filter(':checked').attr('data-deviceprofileid'); var deviceProfileId = dialogInputs.filter(':checked').val();
if (deviceProfileId) { if (deviceProfileId) {
var $this = $(this); var $this = $(this);
$this.dialog("disable"); $this.dialog("disable");
$this.dialog("option", "buttons", null); $this.dialog("option", "buttons", null);
window.location.href = button.attr('href') + '&DeviceProfileId=' + deviceProfileId; $this.find('form').submit();
} else { } else {
alert('A device profile must be selected'); alert('A device profile must be selected');
} }
@@ -447,12 +454,15 @@
} }
} }
}); });
dialogInputs = buttonDialog.find('input'); dialogInputs = buttonDialog.find('input[type="radio"]');
dialogContainers = dialogInputs.closest('li'); dialogContainers = dialogInputs.closest('li');
dialogInputs.change(function () { dialogInputs.change(function () {
const $this = $(this);
dialogContainers.removeClass('selected'); dialogContainers.removeClass('selected');
$(this).closest('li').addClass('selected');
$this.closest('li').addClass('selected');
$('#deviceProfileMoveOrganisationalUnit').prop('checked', $this.attr('data-ouenforced') === 'True');
}); });
buttonDialog.find('.show-decommissioned') buttonDialog.find('.show-decommissioned')
.click(function (e) { .click(function (e) {
File diff suppressed because it is too large Load Diff