Certificate/wireless plugins; major refactoring

Migrate much of BI to Services.
Added Wireless Profile Provider plugin feature.
Added Certificate Authority Provider plugin feature.
Modified Certificate Provider plugin feature.
Database migration v17, for Device Profiles.
Enrolment Client Updated to support CA Certificates, Wireless Profiles
and Hardware Info.
New Client Enrolment Protocol to support new features.
Plugin Manifest Generator added to main solution.
Improved AD search performance.
This commit is contained in:
Gary Sharp
2016-09-28 20:16:25 +10:00
parent 489a5df7cc
commit 27c21175d7
210 changed files with 9822 additions and 6675 deletions
@@ -0,0 +1,192 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Services.Authorization;
using Disco.Services.Interop.ActiveDirectory;
using Disco.Services.Users;
using System;
using System.Linq;
namespace Disco.Services
{
public static class DeviceActionExtensions
{
public static bool IsDecommissioned(this Device d)
{
return d.DecommissionedDate.HasValue;
}
public static bool CanCreateJob(this Device d)
{
if (!JobActionExtensions.CanCreate())
return false;
return !d.IsDecommissioned();
}
public static bool CanUpdateAssignment(this Device d)
{
if (!UserService.CurrentAuthorization.Has(Claims.Device.Actions.AssignUser))
return false;
return !d.IsDecommissioned();
}
public static bool CanUpdateDeviceProfile(this Device d)
{
if (!UserService.CurrentAuthorization.Has(Claims.Device.Properties.DeviceProfile))
return false;
return !d.IsDecommissioned();
}
public static bool CanUpdateDeviceBatch(this Device d)
{
if (!UserService.CurrentAuthorization.Has(Claims.Device.Properties.DeviceBatch))
return false;
return !d.IsDecommissioned();
}
public static bool CanUpdateTrustEnrol(this Device d)
{
if (!UserService.CurrentAuthorization.Has(Claims.Device.Actions.AllowUnauthenticatedEnrol))
return false;
return !d.IsDecommissioned() && !d.AllowUnauthenticatedEnrol;
}
public static bool CanUpdateUntrustEnrol(this Device d)
{
if (!UserService.CurrentAuthorization.Has(Claims.Device.Actions.AllowUnauthenticatedEnrol))
return false;
return !d.IsDecommissioned() && d.AllowUnauthenticatedEnrol;
}
#region Decommission
public static bool CanDecommission(this Device d)
{
if (!UserService.CurrentAuthorization.Has(Claims.Device.Actions.Decommission))
return false;
if (d.DecommissionedDate.HasValue)
return false; // Already Decommissioned
if (d.AssignedUserId != null)
return false; // User Assigned to Device
if (d.Jobs.Count(j => !j.ClosedDate.HasValue) > 0)
return false; // Device linked to > 0 Open Jobs
return true;
}
public static void OnDecommission(this Device d, Disco.Models.Repository.DecommissionReasons Reason)
{
if (!d.CanDecommission())
throw new InvalidOperationException("Decommission of Device is Denied");
d.DecommissionedDate = DateTime.Now;
d.DecommissionReason = Reason;
// Disable AD Account
if (ActiveDirectory.IsValidDomainAccountId(d.DeviceDomainId))
{
var adAccount = d.ActiveDirectoryAccount();
if (adAccount != null && !adAccount.IsCriticalSystemObject)
{
adAccount.DisableAccount();
}
}
}
#endregion
#region Recommission
public static bool CanRecommission(this Device d)
{
if (!UserService.CurrentAuthorization.Has(Claims.Device.Actions.Recommission))
return false;
return d.DecommissionedDate.HasValue;
}
public static void OnRecommission(this Device d)
{
if (!d.CanRecommission())
throw new InvalidOperationException("Recommission of Device is Denied");
d.DecommissionedDate = null;
d.DecommissionReason = null;
// Enable AD Account
if (ActiveDirectory.IsValidDomainAccountId(d.DeviceDomainId))
{
var adAccount = d.ActiveDirectoryAccount();
if (adAccount != null && !adAccount.IsCriticalSystemObject)
{
adAccount.EnableAccount();
}
}
}
#endregion
#region Delete
public static bool CanDelete(this Device d)
{
if (!UserService.CurrentAuthorization.Has(Claims.Device.Actions.Delete))
return false;
return d.DecommissionedDate.HasValue;
}
public static void OnDelete(this Device d, DiscoDataContext Database)
{
// Delete Jobs
foreach (Job j in Database.Jobs.Where(i => i.DeviceSerialNumber == d.SerialNumber))
{
if (j.UserId == null)
{ // No User associated, thus must Delete whole Job
if (j.CanDelete())
j.OnDelete(Database);
else
throw new InvalidOperationException(string.Format("Deletion of Device is Denied (See Job# {0})", j.Id));
}
else
{
// User associated to Job, thus just remove Devices' association
j.DeviceSerialNumber = null;
// Write Job Log
JobLog jobLog = new JobLog()
{
JobId = j.Id,
TechUserId = UserService.CurrentUser.UserId,
Timestamp = DateTime.Now,
Comments = string.Format("# Device Deleted\r\n\r\nSerial Number: **{0}**\r\nComputer Name: **{1}**\r\nModel: **{2}**\r\nProfile: **{3}**",
d.SerialNumber, d.DeviceDomainId, d.DeviceModel, d.DeviceProfile)
};
Database.JobLogs.Add(jobLog);
}
}
// Disable Wireless Certificates
foreach (var wc in Database.DeviceCertificates.Where(i => i.DeviceSerialNumber == d.SerialNumber))
{
wc.DeviceSerialNumber = null;
wc.Enabled = false;
}
// Delete Device Details
foreach (var dd in Database.DeviceDetails.Where(i => i.DeviceSerialNumber == d.SerialNumber))
Database.DeviceDetails.Remove(dd);
// Delete Device Attachments
foreach (var da in Database.DeviceAttachments.Where(i => i.DeviceSerialNumber == d.SerialNumber))
{
da.RepositoryDelete(Database);
Database.DeviceAttachments.Remove(da);
}
// Delete Device User Assignments
foreach (var dua in Database.DeviceUserAssignments.Where(i => i.DeviceSerialNumber == d.SerialNumber))
Database.DeviceUserAssignments.Remove(dua);
Database.Devices.Remove(d);
}
#endregion
}
}
@@ -0,0 +1,40 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Services.Authorization;
using Disco.Services.Devices.ManagedGroups;
using Disco.Services.Interop.ActiveDirectory;
using Disco.Services.Users;
using System;
using System.Linq;
namespace Disco.Services
{
public static class DeviceBatchExtensions
{
public static bool CanDelete(this DeviceBatch db, DiscoDataContext Database)
{
if (!UserService.CurrentAuthorization.Has(Claims.Config.DeviceBatch.Delete))
return false;
// Can't Delete if Contains Devices
var deviceCount = Database.Devices.Count(d => d.DeviceBatchId == db.Id);
if (deviceCount > 0)
return false;
return true;
}
public static void Delete(this DeviceBatch db, DiscoDataContext Database)
{
if (!db.CanDelete(Database))
throw new InvalidOperationException("The state of this Device Batch doesn't allow it to be deleted");
// Remove Linked Group
ActiveDirectory.Context.ManagedGroups.Remove(DeviceBatchDevicesManagedGroup.GetKey(db));
ActiveDirectory.Context.ManagedGroups.Remove(DeviceBatchAssignedUsersManagedGroup.GetKey(db));
// Delete Batch
Database.DeviceBatches.Remove(db);
}
}
}
+19
View File
@@ -0,0 +1,19 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using System;
namespace Disco.Services.Devices
{
public static class DeviceBatches
{
public static DeviceBatch DefaultNewDeviceBatch(DiscoDataContext Database)
{
return new DeviceBatch()
{
PurchaseDate = DateTime.Today
};
}
}
}
@@ -0,0 +1,116 @@
using Disco.Data.Repository;
using Disco.Models.ClientServices;
using Disco.Models.ClientServices.EnrolmentInformation;
using Disco.Models.Repository;
using Disco.Services.Plugins.Features.CertificateAuthorityProvider;
using Disco.Services.Plugins.Features.CertificateProvider;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
namespace Disco.Services
{
public static class DeviceCertificateExtensions
{
public static CertificateStore ProvisionCertificates(this Device device, DiscoDataContext Database, Enrol Enrolment, out List<DeviceCertificate> ProvisionedCertificates)
{
var personalCertificates = new List<byte[]>();
var personalCertificatesRemove = new List<string>();
var intermediateCertificates = new List<byte[]>();
var intermediateCertificatesRemove = new List<string>();
var trustedRootCertificates = new List<byte[]>();
var trustedRootCertificatesRemove = new List<string>();
ProvisionedCertificates = new List<DeviceCertificate>();
foreach (var pluginFeature in device.DeviceProfile.GetCertificateProviders())
{
using (var providerFeature = pluginFeature.CreateInstance<CertificateProviderFeature>())
{
var personalResult = providerFeature.ProvisionPersonalCertificate(Database, device, Enrolment);
if (personalResult != null)
{
if (personalResult.AllocatedCertificates != null && personalResult.AllocatedCertificates.Count > 0)
{
// Avoid transporting certificates if they are already installed
foreach (var certificate in personalResult.AllocatedCertificates)
{
var x509Certificate = new X509Certificate2(certificate.Content, "password");
if (!Enrolment.Certificates.Any(c => c.Thumbprint.Equals(x509Certificate.Thumbprint, StringComparison.OrdinalIgnoreCase)))
{
personalCertificates.Add(certificate.Content);
ProvisionedCertificates.Add(certificate);
}
}
}
if (personalResult.RemoveCertificateThumbprints != null && personalResult.RemoveCertificateThumbprints.Count > 0)
{
personalCertificatesRemove.AddRange(personalResult.RemoveCertificateThumbprints);
}
}
}
}
foreach (var pluginFeature in device.DeviceProfile.GetCertificateAuthorityProviders())
{
using (var providerFeature = pluginFeature.CreateInstance<CertificateAuthorityProviderFeature>())
{
var caResult = providerFeature.ProvisionAuthorityCertificates(Database, device, Enrolment);
if (caResult.TrustedRootCertificates != null && caResult.TrustedRootCertificates.Count > 0)
{
trustedRootCertificates.AddRange(caResult.TrustedRootCertificates);
}
if (caResult.TrustedRootCertificateRemoveThumbprints != null && caResult.TrustedRootCertificateRemoveThumbprints.Count > 0)
{
trustedRootCertificatesRemove.AddRange(caResult.TrustedRootCertificateRemoveThumbprints);
}
if (caResult.IntermediateCertificates != null && caResult.IntermediateCertificates.Count > 0)
{
intermediateCertificates.AddRange(caResult.IntermediateCertificates);
}
if (caResult.IntermediateCertificateRemoveThumbprints != null && caResult.IntermediateCertificateRemoveThumbprints.Count > 0)
{
intermediateCertificatesRemove.AddRange(caResult.IntermediateCertificateRemoveThumbprints);
}
}
}
if (personalCertificates.Count == 0 && personalCertificatesRemove.Count == 0 &&
intermediateCertificates.Count == 0 && intermediateCertificatesRemove.Count == 0 &&
trustedRootCertificates.Count == 0 && trustedRootCertificatesRemove.Count == 0)
{
return null;
}
else
{
return new CertificateStore()
{
TrustedRootCertificates = trustedRootCertificates.Count > 0 ? trustedRootCertificates : null,
TrustedRootRemoveThumbprints = trustedRootCertificatesRemove.Count > 0 ? trustedRootCertificatesRemove : null,
IntermediateCertificates = intermediateCertificates.Count > 0 ? intermediateCertificates : null,
IntermediateRemoveThumbprints = intermediateCertificatesRemove.Count > 0 ? intermediateCertificatesRemove : null,
PersonalCertificates = personalCertificates.Count > 0 ? personalCertificates : null,
PersonalRemoveThumbprints = personalCertificatesRemove.Count > 0 ? personalCertificatesRemove : null
};
}
}
public static DateTime? CertificateExpirationDate(this DeviceCertificate wc)
{
if (wc.Content == null || wc.Content.Length == 0)
{
return null;
}
var c = new X509Certificate2(wc.Content, "password");
return c.NotAfter;
}
}
}
@@ -0,0 +1,159 @@
using Disco.Models.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Disco.Services
{
public static class DeviceDetailExtensions
{
#region Helpers
private static string GetDetail(this IEnumerable<DeviceDetail> details, string Scope, string Key)
{
if (details == null)
throw new ArgumentNullException("details");
if (string.IsNullOrEmpty(Scope))
throw new ArgumentNullException("Scope");
if (string.IsNullOrEmpty(Key))
throw new ArgumentNullException("Key");
var detail = details.Where(d => d.Key == Key).FirstOrDefault();
if (detail == null)
return null;
else
return detail.Value;
}
private static void SetDetail(this Device device, string Scope, string Key, string Value)
{
if (device == null)
throw new ArgumentNullException("device");
if (string.IsNullOrEmpty(Scope))
throw new ArgumentNullException("Scope");
if (string.IsNullOrEmpty(Key))
throw new ArgumentNullException("Key");
var detail = device.DeviceDetails.Where(d => d.Scope == Scope && d.Key == Key).FirstOrDefault();
// No Detail Stored & Set to Null
if (detail == null && Value == null)
return;
if (detail == null)
{
detail = new DeviceDetail()
{
DeviceSerialNumber = device.SerialNumber,
Scope = Scope,
Key = Key,
Value = Value
};
device.DeviceDetails.Add(detail);
}
if (detail.Value != Value)
{
if (Value == null)
{
device.DeviceDetails.Remove(detail);
}
else
{
detail.Value = Value;
}
}
}
#endregion
#region LanMacAddress
/// <summary>
/// Gets the LanMacAddress Device Detail Value
/// </summary>
/// <returns>The LanMacAddress or null</returns>
public static string LanMacAddress(this IEnumerable<DeviceDetail> details)
{
return details.GetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyLanMacAddress);
}
/// <summary>
/// Sets the LanMacAddress Device Detail Value
/// </summary>
public static void LanMacAddress(this IEnumerable<DeviceDetail> details, Device device, string LanMacAddress)
{
device.SetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyLanMacAddress, LanMacAddress);
}
#endregion
#region WLanMacAddress
/// <summary>
/// Gets the WLanMacAddress Device Detail Value
/// </summary>
/// <returns>The WLanMacAddress or null</returns>
public static string WLanMacAddress(this IEnumerable<DeviceDetail> details)
{
return details.GetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyWLanMacAddress);
}
/// <summary>
/// Sets the WLanMacAddress Device Detail Value
/// </summary>
public static void WLanMacAddress(this IEnumerable<DeviceDetail> details, Device device, string WLanMacAddress)
{
device.SetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyWLanMacAddress, WLanMacAddress);
}
#endregion
#region ACAdapter
/// <summary>
/// Gets the ACAdapter Device Detail Value
/// </summary>
/// <returns>The ACAdapter or null</returns>
public static string ACAdapter(this IEnumerable<DeviceDetail> details)
{
return details.GetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyACAdapter);
}
/// <summary>
/// Sets the ACAdapter Device Detail Value
/// </summary>
public static void ACAdapter(this IEnumerable<DeviceDetail> details, Device device, string ACAdapter)
{
device.SetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyACAdapter, ACAdapter);
}
#endregion
#region Battery
/// <summary>
/// Gets the Battery Device Detail Value
/// </summary>
/// <returns>The Battery or null</returns>
public static string Battery(this IEnumerable<DeviceDetail> details)
{
return details.GetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyBattery);
}
/// <summary>
/// Sets the Battery Device Detail Value
/// </summary>
public static void Battery(this IEnumerable<DeviceDetail> details, Device device, string Battery)
{
device.SetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyBattery, Battery);
}
#endregion
#region Keyboard
/// <summary>
/// Gets the Keyboard Device Detail Value
/// </summary>
/// <returns>The Keyboard or null</returns>
public static string Keyboard(this IEnumerable<DeviceDetail> details)
{
return details.GetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyKeyboard);
}
/// <summary>
/// Sets the Keyboard Device Detail Value
/// </summary>
public static void Keyboard(this IEnumerable<DeviceDetail> details, Device device, string Keyboard)
{
device.SetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyKeyboard, Keyboard);
}
#endregion
}
}
+214
View File
@@ -0,0 +1,214 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Documents;
using Disco.Services.Authorization;
using Disco.Services.Expressions;
using Disco.Services.Interop.ActiveDirectory;
using Disco.Services.Users;
using Exceptionless;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Services
{
public static class DeviceExtensions
{
public static string ComputerNameRender(this Device device, DiscoDataContext Database, ADDomain Domain)
{
if (Domain == null)
throw new ArgumentNullException("Domain");
var deviceProfile = device.DeviceProfile;
Expression computerNameTemplateExpression = null;
computerNameTemplateExpression = ExpressionCache.GetValue(DeviceProfileExtensions.ComputerNameExpressionCacheModule, deviceProfile.Id.ToString(), () =>
{
// Removed 2012-06-14 G# - Properties moved to DeviceProfile model & DB Migrated in DBv3.
//return Expressions.Expression.TokenizeSingleDynamic(null, deviceProfile.Configuration(context).ComputerNameTemplate, 0);
return Expression.TokenizeSingleDynamic(null, deviceProfile.ComputerNameTemplate, 0);
});
var evaluatorVariables = Expression.StandardVariables(null, Database, UserService.CurrentUser, DateTime.Now, null);
string rendered;
try
{
rendered = computerNameTemplateExpression.EvaluateFirst<string>(device, evaluatorVariables);
}
catch (Exception ex)
{
ex.ToExceptionless().AddObject(deviceProfile.ComputerNameTemplate, "ComputerNameTemplate").Submit();
throw new InvalidOperationException(string.Format("An error occurred rendering the computer name: [{0}] {1}", ex.GetType().Name, ex.Message), ex.InnerException);
}
if (rendered == null || rendered.Length > 24)
{
throw new InvalidOperationException("The rendered computer name would be invalid or longer than 24 characters");
}
return string.Format(@"{0}\{1}", Domain.NetBiosName, rendered);
}
public static List<DocumentTemplate> AvailableDocumentTemplates(this Device d, DiscoDataContext Database, User User, DateTime TimeStamp)
{
List<DocumentTemplate> ats = Database.DocumentTemplates
.Where(at => at.Scope == DocumentTemplate.DocumentTemplateScopes.Device).ToList();
return ats.Where(at => at.FilterExpressionMatches(d, Database, User, TimeStamp, DocumentState.DefaultState())).ToList();
}
public static bool UpdateLastNetworkLogonDate(this Device Device)
{
return ADNetworkLogonDatesUpdateTask.UpdateLastNetworkLogonDate(Device);
}
public static Device AddOffline(this Device d, DiscoDataContext Database)
{
// Just Include:
// - Serial Number
// - Asset Number
// - Profile Id
// - Assigned User Id
// - Batch
// Enforce Authorization
var auth = UserService.CurrentAuthorization;
if (!auth.Has(Claims.Device.Properties.AssetNumber))
d.AssetNumber = null;
if (!auth.Has(Claims.Device.Properties.Location))
d.Location = null;
if (!auth.Has(Claims.Device.Properties.DeviceBatch))
d.DeviceBatchId = null;
if (!auth.Has(Claims.Device.Properties.DeviceProfile))
d.DeviceProfileId = Database.DiscoConfiguration.DeviceProfiles.DefaultAddDeviceOfflineDeviceProfileId;
if (!auth.Has(Claims.Device.Actions.AssignUser))
d.AssignedUserId = null;
// Batch
DeviceBatch db = default(DeviceBatch);
if (d.DeviceBatchId.HasValue)
db = Database.DeviceBatches.Find(d.DeviceBatchId.Value);
// Default Device Model
DeviceModel dm = default(DeviceModel);
if (db != null && db.DefaultDeviceModelId.HasValue)
dm = Database.DeviceModels.Find(db.DefaultDeviceModelId); // From Batch
else
dm = Database.DeviceModels.Find(1); // Default
Device d2 = new Device()
{
SerialNumber = d.SerialNumber.ToUpper(),
AssetNumber = d.AssetNumber,
Location = d.Location,
CreatedDate = DateTime.Now,
DeviceProfileId = d.DeviceProfileId,
DeviceProfile = Database.DeviceProfiles.Find(d.DeviceProfileId),
AllowUnauthenticatedEnrol = true,
DeviceModelId = dm.Id,
DeviceModel = dm,
DeviceBatchId = d.DeviceBatchId,
DeviceBatch = db
};
Database.Devices.Add(d2);
if (!string.IsNullOrEmpty(d.AssignedUserId))
{
User u = UserService.GetUser(ActiveDirectory.ParseDomainAccountId(d.AssignedUserId), Database, true);
d2.AssignDevice(Database, u);
}
return d2;
}
public static DeviceUserAssignment AssignDevice(this Device d, DiscoDataContext Database, User u)
{
DeviceUserAssignment newDua = default(DeviceUserAssignment);
// Mark existing assignments as Unassigned
foreach (var dua in Database.DeviceUserAssignments.Where(m => m.DeviceSerialNumber == d.SerialNumber && !m.UnassignedDate.HasValue))
dua.UnassignedDate = DateTime.Now;
if (u != null)
{
// Add new Assignment
newDua = new DeviceUserAssignment()
{
DeviceSerialNumber = d.SerialNumber,
AssignedUserId = u.UserId,
AssignedDate = DateTime.Now
};
Database.DeviceUserAssignments.Add(newDua);
d.AssignedUserId = u.UserId;
d.AssignedUser = u;
}
else
{
d.AssignedUserId = null;
}
// Update AD Account
if (ActiveDirectory.IsValidDomainAccountId(d.DeviceDomainId))
{
var adMachineAccount = ActiveDirectory.RetrieveADMachineAccount(d.DeviceDomainId);
if (adMachineAccount != null)
adMachineAccount.SetDescription(d);
}
return newDua;
}
public static string ReasonMessage(this DecommissionReasons r)
{
switch (r)
{
case DecommissionReasons.EndOfLife:
return "End of Life";
case DecommissionReasons.Sold:
return "Sold";
case DecommissionReasons.Stolen:
return "Stolen";
case DecommissionReasons.Lost:
return "Lost";
case DecommissionReasons.Damaged:
return "Damaged";
case DecommissionReasons.Donated:
return "Donated";
case DecommissionReasons.Returned:
return "Returned";
default:
return "Unknown";
}
}
public static string ReasonMessage(this DecommissionReasons? r)
{
if (!r.HasValue)
return "Not Decommissioned";
return r.Value.ReasonMessage();
}
public static string StatusCode(this Device Device)
{
if (Device.DecommissionedDate.HasValue)
return "Decommissioned";
if (!Device.EnrolledDate.HasValue)
return "NotEnrolled";
return "Active";
}
public static string Status(this Device Device)
{
if (Device.DecommissionedDate.HasValue)
return string.Format("Decommissioned ({0})", Device.DecommissionReason.ReasonMessage());
if (!Device.EnrolledDate.HasValue)
return "Not Enrolled";
return "Active";
}
}
}
@@ -0,0 +1,96 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Services.Authorization;
using Disco.Services.Users;
using System;
using System.Data.Entity;
using System.IO;
using System.Linq;
namespace Disco.Services
{
public static class DeviceModelExtensions
{
private static object _CreateDeviceModelLock = new object();
public static bool CanDelete(this DeviceModel dm, DiscoDataContext Database)
{
if (!UserService.CurrentAuthorization.Has(Claims.Config.DeviceModel.Delete))
return false;
// Can't Delete Default Model (Id: 1)
if (dm.Id == 1)
return false;
// Can't Delete if Contains Devices
if (Database.Devices.Count(d => d.DeviceModelId == dm.Id) > 0)
return false;
return true;
}
public static void Delete(this DeviceModel dm, DiscoDataContext Database)
{
if (!dm.CanDelete(Database))
throw new InvalidOperationException("The state of this Device Model doesn't allow it to be deleted");
// Delete Image
var deviceModelImagePath = dm.ImageFilePath();
if (File.Exists(deviceModelImagePath))
File.Delete(deviceModelImagePath);
// Delete any Device Model Components
foreach (var deviceModelComponent in Database.DeviceComponents.Where(dc => dc.DeviceModelId == dm.Id).ToList())
{
Database.DeviceComponents.Remove(deviceModelComponent);
}
// Delete Model
Database.DeviceModels.Remove(dm);
}
public static Tuple<DeviceModel, bool> GetOrCreateDeviceModel(this DbSet<DeviceModel> DeviceModelsSet, string Manufacturer, string Model, string ModelType)
{
// Already Exists?
var deviceModel = DeviceModelsSet.FirstOrDefault(dm => dm.Manufacturer == Manufacturer && dm.Model == Model);
if (deviceModel == null)
{
// Ensure only one thread/request at a time
lock (_CreateDeviceModelLock)
{
// Check again now that lock is enforced
deviceModel = DeviceModelsSet.FirstOrDefault(dm => dm.Manufacturer == Manufacturer && dm.Model == Model);
if (deviceModel == null)
{
// Create the Device Model in a different DataContext so we don't have to commit unrelated changes
using (DiscoDataContext database = new DiscoDataContext())
{
var addDeviceModel = new DeviceModel
{
Manufacturer = Manufacturer,
Model = Model,
ModelType = ModelType,
Description = string.Format("{0} {1}", Manufacturer, Model)
};
database.DeviceModels.Add(addDeviceModel);
database.SaveChanges();
}
// Obtain the Device Model with the in-scope DataContext
// - Overhead acknowledged, but reasonable given the infrequency of occurrence
deviceModel = DeviceModelsSet.FirstOrDefault(dm => dm.Manufacturer == Manufacturer && dm.Model == Model);
return new Tuple<DeviceModel, bool>(deviceModel, true);
}
}
}
else
{
if (deviceModel.ModelType != ModelType)
deviceModel.ModelType = ModelType;
}
return new Tuple<DeviceModel, bool>(deviceModel, false);
}
}
}
@@ -0,0 +1,119 @@
using Disco.Data.Repository;
using Disco.Models.BI.Config;
using Disco.Models.Repository;
using Disco.Services.Authorization;
using Disco.Services.Devices.ManagedGroups;
using Disco.Services.Expressions;
using Disco.Services.Interop.ActiveDirectory;
using Disco.Services.Plugins;
using Disco.Services.Plugins.Features.CertificateAuthorityProvider;
using Disco.Services.Plugins.Features.CertificateProvider;
using Disco.Services.Plugins.Features.WirelessProfileProvider;
using Disco.Services.Users;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Disco.Services
{
public static class DeviceProfileExtensions
{
public const string ComputerNameExpressionCacheModule = "ComputerNameTemplate";
public static void ComputerNameInvalidateCache(this DeviceProfile deviceProfile)
{
ExpressionCache.InvalidateKey(ComputerNameExpressionCacheModule, deviceProfile.Id.ToString());
}
public static OrganisationAddress DefaultOrganisationAddressDetails(this DeviceProfile deviceProfile, DiscoDataContext Database)
{
if (deviceProfile.DefaultOrganisationAddress.HasValue)
{
return Database.DiscoConfiguration.OrganisationAddresses.GetAddress(deviceProfile.DefaultOrganisationAddress.Value);
}
else
{
return null;
}
}
public static bool CanDelete(this DeviceProfile dp, DiscoDataContext Database)
{
if (!UserService.CurrentAuthorization.Has(Claims.Config.DeviceProfile.Delete))
return false;
// Can't Delete Default Profile (Id: 1)
if (dp.Id == 1)
return false;
// Can't Delete if Contains Devices
if (Database.Devices.Count(d => d.DeviceProfileId == dp.Id) > 0)
return false;
return true;
}
public static void Delete(this DeviceProfile dp, DiscoDataContext Database)
{
if (!dp.CanDelete(Database))
throw new InvalidOperationException("The state of this Device Profile doesn't allow it to be deleted");
// Update Defaults
if (Database.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId == dp.Id)
Database.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId = 1;
if (Database.DiscoConfiguration.DeviceProfiles.DefaultAddDeviceOfflineDeviceProfileId == dp.Id)
Database.DiscoConfiguration.DeviceProfiles.DefaultAddDeviceOfflineDeviceProfileId = 1;
// Remove Linked Group
ActiveDirectory.Context.ManagedGroups.Remove(DeviceProfileDevicesManagedGroup.GetKey(dp));
ActiveDirectory.Context.ManagedGroups.Remove(DeviceProfileAssignedUsersManagedGroup.GetKey(dp));
// Delete Profile
Database.DeviceProfiles.Remove(dp);
}
public static IEnumerable<PluginFeatureManifest> GetCertificateProviders(this DeviceProfile dp)
{
if (!string.IsNullOrEmpty(dp.CertificateProviders))
{
foreach (var certificateProviderId in dp.CertificateProviders.Split(','))
{
PluginFeatureManifest featureManifest = null;
if (Plugins.Plugins.TryGetPluginFeature(certificateProviderId.Trim(), typeof(CertificateProviderFeature), out featureManifest))
{
yield return featureManifest;
}
}
}
}
public static IEnumerable<PluginFeatureManifest> GetCertificateAuthorityProviders(this DeviceProfile dp)
{
if (!string.IsNullOrEmpty(dp.CertificateAuthorityProviders))
{
foreach (var certificateAuthorityProviderId in dp.CertificateAuthorityProviders.Split(','))
{
PluginFeatureManifest featureManifest = null;
if (Plugins.Plugins.TryGetPluginFeature(certificateAuthorityProviderId.Trim(), typeof(CertificateAuthorityProviderFeature), out featureManifest))
{
yield return featureManifest;
}
}
}
}
public static IEnumerable<PluginFeatureManifest> GetWirelessProfileProviders(this DeviceProfile dp)
{
if (!string.IsNullOrEmpty(dp.WirelessProfileProviders))
{
foreach (var wirelessProfileProviderId in dp.WirelessProfileProviders.Split(','))
{
PluginFeatureManifest featureManifest = null;
if (Plugins.Plugins.TryGetPluginFeature(wirelessProfileProviderId.Trim(), typeof(WirelessProfileProviderFeature), out featureManifest))
{
yield return featureManifest;
}
}
}
}
}
}
+4 -6
View File
@@ -1,16 +1,14 @@
using Disco.Data.Repository.Monitor;
using Disco.Data.Repository;
using Disco.Data.Repository.Monitor;
using Disco.Models.Repository;
using Disco.Services.Authorization;
using Disco.Services.Web.Signalling;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reactive.Linq;
using Disco.Models.Repository;
using Disco.Data.Repository;
using System.Threading.Tasks;
namespace Disco.Services.Devices
{
@@ -0,0 +1,67 @@
using Disco.Data.Repository;
using Disco.Models.ClientServices;
using Disco.Models.ClientServices.EnrolmentInformation;
using Disco.Models.Repository;
using Disco.Services.Plugins.Features.WirelessProfileProvider;
using System.Collections.Generic;
using System.Linq;
namespace Disco.Services
{
public static class DeviceWirelessProfileExtensions
{
public static WirelessProfileStore ProvisionWirelessProfiles(this Device device, DiscoDataContext Database, Enrol Enrolment)
{
var profiles = new List<Models.ClientServices.EnrolmentInformation.WirelessProfile>();
var transformations = new List<Models.ClientServices.EnrolmentInformation.WirelessProfileTransformation>();
var removeNames = new List<string>();
foreach (var pluginFeature in device.DeviceProfile.GetWirelessProfileProviders())
{
using (var providerFeature = pluginFeature.CreateInstance<WirelessProfileProviderFeature>())
{
var pluginResult = providerFeature.ProvisionWirelessProfiles(Database, device, Enrolment);
if (pluginResult.Profiles != null)
{
profiles.AddRange(pluginResult.Profiles.Select(p => new Models.ClientServices.EnrolmentInformation.WirelessProfile()
{
Name = p.Name,
ProfileXml = p.ProfileXml,
ForceDeployment = p.ForceDeployment
}));
}
if (pluginResult.Transformations != null)
{
transformations.AddRange(pluginResult.Transformations.Select(p => new Models.ClientServices.EnrolmentInformation.WirelessProfileTransformation()
{
Name = p.Name,
RegularExpression = p.RegularExpression,
RegularExpressionReplacement = p.RegularExpressionReplacement
}));
}
if (pluginResult.RemoveNames != null)
{
removeNames.AddRange(pluginResult.RemoveNames);
}
}
}
if (profiles.Count == 0 && transformations.Count == 0 && removeNames.Count == 0)
{
return null;
}
else
{
return new WirelessProfileStore()
{
Profiles = profiles.Count > 0 ? profiles : null,
Transformations = transformations.Count > 0 ? transformations : null,
RemoveNames = removeNames.Count > 0 ? removeNames : null
};
}
}
}
}
@@ -0,0 +1,647 @@
using Disco.Data.Repository;
using Disco.Models.ClientServices;
using Disco.Models.Repository;
using Disco.Services.Authorization;
using Disco.Services.Interop.ActiveDirectory;
using Disco.Services.Users;
using Exceptionless;
using PList;
using Renci.SshNet;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
namespace Disco.Services.Devices.Enrolment
{
public static class DeviceEnrolment
{
public static MacSecureEnrolResponse MacSecureEnrol(DiscoDataContext Database, string Host)
{
MacEnrol trustedRequest = new MacEnrol();
string sessionId = System.Guid.NewGuid().ToString("B");
MacSecureEnrolResponse MacSecureEnrol;
try
{
EnrolmentLog.LogSessionStarting(sessionId, Host, EnrolmentTypes.MacSecure);
EnrolmentLog.LogSessionProgress(sessionId, 0, $"Connecting to '{Host}' as '{Database.DiscoConfiguration.Bootstrapper.MacSshUsername}'");
var sshConnectionInfo = new KeyboardInteractiveConnectionInfo(Host, Database.DiscoConfiguration.Bootstrapper.MacSshUsername);
sshConnectionInfo.AuthenticationPrompt += (sender, e) =>
{
foreach (var prompt in e.Prompts)
{
if (prompt.Request.StartsWith("Password", StringComparison.OrdinalIgnoreCase))
{
EnrolmentLog.LogSessionProgress(sessionId, 10, $"Authenticating at '{Host}' as '{Database.DiscoConfiguration.Bootstrapper.MacSshUsername}'");
prompt.Response = Database.DiscoConfiguration.Bootstrapper.MacSshPassword;
}
}
};
using (var sshClient = new SshClient(sshConnectionInfo))
{
sshClient.Connect();
try
{
EnrolmentLog.LogSessionProgress(sessionId, 30, "Retrieving System Profile Information");
var sshResult = sshClient.RunCommand("system_profiler -xml SPHardwareDataType SPNetworkDataType SPSoftwareDataType");
PListRoot profilerData;
using (var reader = new StringReader(sshResult.Result))
{
using (var xmlReader = XmlReader.Create(reader, new XmlReaderSettings() { DtdProcessing = DtdProcessing.Ignore }))
{
XmlSerializer serializer = new XmlSerializer(typeof(PListRoot));
profilerData = (PListRoot)serializer.Deserialize(xmlReader);
}
}
EnrolmentLog.LogSessionProgress(sessionId, 90, "Processing System Profile Information");
PListDict profilerDataHardware = null;
PListArray profilerDataNetwork = null;
PListDict profilerDataSoftware = null;
foreach (PListDict node in (profilerData.Root as PListArray))
{
var nodeItems = ((PListArray)node["_items"]);
switch (((PListString)node["_dataType"]).Value)
{
case "SPHardwareDataType":
profilerDataHardware = (PListDict)nodeItems[0];
break;
case "SPNetworkDataType":
profilerDataNetwork = nodeItems;
break;
case "SPSoftwareDataType":
profilerDataSoftware = (PListDict)nodeItems[0];
break;
}
}
if (profilerDataHardware == null || profilerDataNetwork == null || profilerDataSoftware == null)
throw new InvalidOperationException("System Profiler didn't return information for a requested data type");
trustedRequest.DeviceSerialNumber = (profilerDataHardware["serial_number"] as PListString).Value;
trustedRequest.DeviceUUID = (profilerDataHardware["platform_UUID"] as PListString).Value;
trustedRequest.DeviceComputerName = (profilerDataSoftware["local_host_name"] as PListString).Value;
var profilerDataNetworkEthernet = profilerDataNetwork.Cast<PListDict>().FirstOrDefault(e => ((PListString)e["_name"]).Value == "Ethernet");
if (profilerDataNetworkEthernet != null)
{
trustedRequest.DeviceLanMacAddress = ((PListString)(profilerDataNetworkEthernet["Ethernet"] as PListDict)["MAC Address"]).Value;
}
var profilerDataNetworkWiFi = profilerDataNetwork.Cast<PListDict>().FirstOrDefault(e => ((PListString)e["_name"]).Value == "Wi-Fi");
if (profilerDataNetworkWiFi != null)
{
trustedRequest.DeviceWlanMacAddress = ((PListString)(profilerDataNetworkWiFi["Ethernet"] as PListDict)["MAC Address"]).Value;
}
trustedRequest.DeviceManufacturer = "Apple Inc.";
trustedRequest.DeviceModel = (profilerDataHardware["machine_model"] as PListString).Value;
trustedRequest.DeviceModelType = ParseMacModelType((profilerDataHardware["machine_name"] as PListString).Value);
EnrolmentLog.LogSessionProgress(sessionId, 99, "Disconnecting");
sshClient.Disconnect();
}
catch (Exception)
{
throw;
}
finally
{
if (sshClient != null)
{
bool connected = sshClient.IsConnected;
if (connected)
{
sshClient.Disconnect();
}
}
}
}
EnrolmentLog.LogSessionProgress(sessionId, 100, "Disconnected, Starting Disco Enrolment");
MacSecureEnrolResponse response = MacSecureEnrolResponse.FromMacEnrolResponse(MacEnrol(Database, trustedRequest, true, sessionId));
EnrolmentLog.LogSessionFinished(sessionId);
MacSecureEnrol = response;
}
catch (System.Exception ex)
{
ex.ToExceptionless().Submit();
EnrolmentLog.LogSessionError(sessionId, ex);
throw ex;
}
return MacSecureEnrol;
}
#region "Mac Enrol Helpers"
private static string ParseMacModelType(string ModelName)
{
string ParseMacModelType;
if (!string.IsNullOrWhiteSpace(ModelName))
{
string mn = ModelName.ToLower();
if (mn.Contains("imac") || mn.Contains("mini"))
{
ParseMacModelType = "Desktop";
return ParseMacModelType;
}
if (mn.Contains("macbook"))
{
ParseMacModelType = "Mobile";
return ParseMacModelType;
}
if (mn.Contains("xserve"))
{
ParseMacModelType = "Server";
return ParseMacModelType;
}
}
ParseMacModelType = "Unknown";
return ParseMacModelType;
}
#endregion
public static MacEnrolResponse MacEnrol(DiscoDataContext Database, MacEnrol Request, bool Trusted, string OpenSessionId = null)
{
string sessionId;
if (OpenSessionId == null)
{
sessionId = System.Guid.NewGuid().ToString("B");
EnrolmentLog.LogSessionStarting(sessionId, Request.DeviceSerialNumber, EnrolmentTypes.Mac);
}
else
{
sessionId = OpenSessionId;
}
EnrolmentLog.LogSessionDeviceInfo(sessionId, Request);
MacEnrolResponse response = new MacEnrolResponse();
try
{
EnrolmentLog.LogSessionProgress(sessionId, 10, "Querying Database");
Device RepoDevice = Database.Devices.Include("AssignedUser").Include("DeviceProfile").Include("DeviceProfile").Where(d => d.SerialNumber == Request.DeviceSerialNumber).FirstOrDefault();
if (!Trusted)
{
if (RepoDevice == null)
throw new EnrolmentSafeException(string.Format("Unknown Device Serial Number (SN: '{0}')", Request.DeviceSerialNumber));
if (!RepoDevice.AllowUnauthenticatedEnrol)
throw new EnrolmentSafeException(string.Format("Device isn't allowed an Unauthenticated Enrolment (SN: '{0}')", Request.DeviceSerialNumber));
}
if (RepoDevice == null)
{
EnrolmentLog.LogSessionProgress(sessionId, 50, "New Device, Building Disco Instance");
EnrolmentLog.LogSessionTaskAddedDevice(sessionId, Request.DeviceSerialNumber);
DeviceProfile deviceProfile = Database.DeviceProfiles.Find(Database.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId);
var deviceModelResult = Database.DeviceModels.GetOrCreateDeviceModel(Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim(), Request.DeviceModelType.Trim());
DeviceModel deviceModel = deviceModelResult.Item1;
if (deviceModelResult.Item2)
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.DeviceSerialNumber, deviceModelResult.Item1.Manufacturer, deviceModelResult.Item1.Model);
else
EnrolmentLog.LogSessionDevice(sessionId, Request.DeviceSerialNumber, deviceModel.Id);
RepoDevice = new Device
{
SerialNumber = Request.DeviceSerialNumber,
DeviceDomainId = Request.DeviceComputerName,
DeviceProfile = deviceProfile,
DeviceModel = deviceModel,
AllowUnauthenticatedEnrol = false,
CreatedDate = DateTime.Now,
EnrolledDate = DateTime.Now
};
Database.Devices.Add(RepoDevice);
}
else
{
EnrolmentLog.LogSessionProgress(sessionId, 50, "Existing Device, Updating Disco Instance");
EnrolmentLog.LogSessionTaskUpdatingDevice(sessionId, Request.DeviceSerialNumber);
var deviceModelResult = Database.DeviceModels.GetOrCreateDeviceModel(Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim(), Request.DeviceModelType.Trim());
DeviceModel deviceModel = deviceModelResult.Item1;
if (deviceModelResult.Item2)
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.DeviceSerialNumber, deviceModelResult.Item1.Manufacturer, deviceModelResult.Item1.Model);
else
EnrolmentLog.LogSessionDevice(sessionId, Request.DeviceSerialNumber, deviceModel.Id);
RepoDevice.DeviceModel = deviceModel;
RepoDevice.DeviceDomainId = Request.DeviceComputerName;
if (!RepoDevice.EnrolledDate.HasValue)
{
RepoDevice.EnrolledDate = DateTime.Now;
}
}
RepoDevice.LastEnrolDate = DateTime.Now;
RepoDevice.AllowUnauthenticatedEnrol = false;
// Removed 2012-06-14 G# - Properties moved to DeviceProfile model & DB Migrated in DBv3.
//DeviceProfileConfiguration RepoDeviceProfileContext = RepoDevice.DeviceProfile.Configuration(Context);
EnrolmentLog.LogSessionProgress(sessionId, 90, "Building Response");
//if (RepoDeviceProfileContext.DistributionType == DeviceProfileConfiguration.DeviceProfileDistributionTypes.OneToOne && RepoDevice.AssignedUser != null)
if (RepoDevice.DeviceProfile.DistributionType == DeviceProfile.DistributionTypes.OneToOne && RepoDevice.AssignedUser != null)
{
ADUserAccount AssignedUserInfo = ActiveDirectory.RetrieveADUserAccount(RepoDevice.AssignedUser.UserId);
EnrolmentLog.LogSessionTaskAssigningUser(sessionId, RepoDevice.SerialNumber, AssignedUserInfo.DisplayName, AssignedUserInfo.SamAccountName, AssignedUserInfo.Domain.NetBiosName, AssignedUserInfo.SecurityIdentifier.ToString());
response.DeviceAssignedUserUsername = AssignedUserInfo.SamAccountName;
response.DeviceAssignedUserDomain = AssignedUserInfo.Domain.NetBiosName;
response.DeviceAssignedUserName = AssignedUserInfo.DisplayName;
response.DeviceAssignedUserSID = AssignedUserInfo.SecurityIdentifier.ToString();
}
response.DeviceComputerName = RepoDevice.DeviceDomainId;
EnrolmentLog.LogSessionProgress(sessionId, 100, "Completed Successfully");
}
catch (EnrolmentSafeException ex)
{
EnrolmentLog.LogSessionError(sessionId, ex);
return new MacEnrolResponse { ErrorMessage = ex.Message };
}
catch (System.Exception ex2)
{
ex2.ToExceptionless().Submit();
EnrolmentLog.LogSessionError(sessionId, ex2);
throw ex2;
}
finally
{
if (OpenSessionId == null)
EnrolmentLog.LogSessionFinished(sessionId);
}
return response;
}
public static EnrolResponse Enrol(DiscoDataContext Database, string Username, Enrol Request)
{
ADMachineAccount adMachineAccount = null;
EnrolResponse response = new EnrolResponse();
AuthorizationToken authenticatedToken = null;
bool isAuthenticated = false;
ADDomain domain = null;
Lazy<ADDomainController> domainController = new Lazy<ADDomainController>(() =>
{
if (domain == null)
throw new InvalidOperationException("The [domain] variable must be initialized first");
return domain.GetAvailableDomainController(RequireWritable: true);
});
string sessionId = System.Guid.NewGuid().ToString("B");
response.SessionId = sessionId;
EnrolmentLog.LogSessionStarting(sessionId, Request.SerialNumber, EnrolmentTypes.Normal);
EnrolmentLog.LogSessionDeviceInfo(sessionId, Request);
try
{
EnrolmentLog.LogSessionProgress(sessionId, 10, "Loading User Data");
if (!string.IsNullOrWhiteSpace(Username))
{
authenticatedToken = UserService.GetAuthorization(Username, Database);
isAuthenticated = (authenticatedToken != null);
}
EnrolmentLog.LogSessionProgress(sessionId, 13, "Loading Device Data");
Device RepoDevice = Database.Devices.Include("AssignedUser").Include("DeviceModel").Include("DeviceProfile").Where(d => d.SerialNumber == Request.SerialNumber).FirstOrDefault();
EnrolmentLog.LogSessionProgress(sessionId, 15, "Discovering User/Device Disco Permissions");
if (isAuthenticated)
{
if (!authenticatedToken.Has(Claims.Device.Actions.EnrolDevices))
{
if (!authenticatedToken.Has(Claims.ComputerAccount))
throw new EnrolmentSafeException(string.Format("Connection not correctly authenticated (SN: {0}; Auth User: {1})", Request.SerialNumber, authenticatedToken.User.UserId));
if (domain == null)
domain = ActiveDirectory.Context.GetDomainByName(Request.DNSDomainName);
if (!authenticatedToken.User.UserId.Equals(string.Format(@"{0}\{1}$", domain.NetBiosName, Request.ComputerName), System.StringComparison.OrdinalIgnoreCase))
throw new EnrolmentSafeException(string.Format("Connection not correctly authenticated (SN: {0}; Auth User: {1})", Request.SerialNumber, authenticatedToken.User.UserId));
}
}
else
{
if (RepoDevice == null)
{
throw new EnrolmentSafeException(string.Format("Unknown Device Serial Number (SN: '{0}')", Request.SerialNumber));
}
if (!RepoDevice.AllowUnauthenticatedEnrol)
{
if (RepoDevice.DeviceProfile.AllowUntrustedReimageJobEnrolment)
{
if (Database.Jobs.Count(j => j.DeviceSerialNumber == RepoDevice.SerialNumber && j.JobTypeId == JobType.JobTypeIds.SImg && !j.ClosedDate.HasValue) == 0)
{
throw new EnrolmentSafeException(string.Format("Device has no open 'Software - Reimage' job (SN: '{0}')", Request.SerialNumber));
}
}
else
{
throw new EnrolmentSafeException(string.Format("Device isn't allowed an Unauthenticated Enrolment (SN: '{0}')", Request.SerialNumber));
}
}
}
if (Request.IsPartOfDomain && !string.IsNullOrWhiteSpace(Request.ComputerName))
{
EnrolmentLog.LogSessionProgress(sessionId, 20, "Loading Active Directory Computer Account");
System.Guid? uuidGuid = null;
System.Guid? macAddressGuid = null;
if (!string.IsNullOrEmpty(Request.Hardware.UUID))
uuidGuid = ADMachineAccount.NetbootGUIDFromUUID(Request.Hardware.UUID);
// Use non-Wlan Adapter with fastest speed
var macAddress = Request.Hardware?.NetworkAdapters?.Where(na => !na.IsWlanAdapter).OrderByDescending(na => na.Speed).Select(na => na.MACAddress).FirstOrDefault();
if (!string.IsNullOrEmpty(macAddress))
macAddressGuid = ADMachineAccount.NetbootGUIDFromMACAddress(macAddress);
if (domain == null)
domain = ActiveDirectory.Context.GetDomainByName(Request.DNSDomainName);
var requestDeviceId = string.Format(@"{0}\{1}", domain.NetBiosName, Request.ComputerName);
adMachineAccount = domainController.Value.RetrieveADMachineAccount(requestDeviceId, uuidGuid, macAddressGuid);
}
if (RepoDevice == null)
{
EnrolmentLog.LogSessionProgress(sessionId, 30, "New Device, Creating Disco Instance");
EnrolmentLog.LogSessionTaskAddedDevice(sessionId, Request.SerialNumber);
DeviceProfile deviceProfile = Database.DeviceProfiles.Find(Database.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId);
var deviceModelResult = Database.DeviceModels.GetOrCreateDeviceModel(Request.Hardware.Manufacturer.Trim(), Request.Hardware.Model.Trim(), Request.Hardware.ModelType.Trim());
DeviceModel deviceModel = deviceModelResult.Item1;
if (deviceModelResult.Item2)
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.SerialNumber, deviceModelResult.Item1.Manufacturer, deviceModelResult.Item1.Model);
else
EnrolmentLog.LogSessionDevice(sessionId, Request.SerialNumber, deviceModel.Id);
if (domain == null)
domain = ActiveDirectory.Context.GetDomainByName(Request.DNSDomainName);
RepoDevice = new Device
{
SerialNumber = Request.SerialNumber,
DeviceDomainId = string.Format(@"{0}\{1}", domain.NetBiosName, Request.ComputerName),
DeviceProfile = deviceProfile,
DeviceModel = deviceModel,
AllowUnauthenticatedEnrol = false,
CreatedDate = DateTime.Now,
EnrolledDate = DateTime.Now,
LastEnrolDate = DateTime.Now,
DeviceDetails = new List<DeviceDetail>()
};
Database.Devices.Add(RepoDevice);
var lanMacAddresses = string.Join("; ", Request.Hardware.NetworkAdapters?.Where(na => !na.IsWlanAdapter).Select(na => na.MACAddress));
var wlanMacAddresses = string.Join("; ", Request.Hardware.NetworkAdapters?.Where(na => na.IsWlanAdapter).Select(na => na.MACAddress));
if (!string.IsNullOrEmpty(lanMacAddresses))
RepoDevice.DeviceDetails.LanMacAddress(RepoDevice, lanMacAddresses);
if (!string.IsNullOrEmpty(wlanMacAddresses))
RepoDevice.DeviceDetails.WLanMacAddress(RepoDevice, wlanMacAddresses);
}
else
{
EnrolmentLog.LogSessionProgress(sessionId, 30, "Existing Device, Updating Disco Instance");
EnrolmentLog.LogSessionTaskUpdatingDevice(sessionId, Request.SerialNumber);
var deviceModelResult = Database.DeviceModels.GetOrCreateDeviceModel(Request.Hardware.Manufacturer.Trim(), Request.Hardware.Model.Trim(), Request.Hardware.ModelType.Trim());
DeviceModel deviceModel = deviceModelResult.Item1;
if (deviceModelResult.Item2)
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.SerialNumber, deviceModelResult.Item1.Manufacturer, deviceModelResult.Item1.Model);
else
EnrolmentLog.LogSessionDevice(sessionId, Request.SerialNumber, deviceModel.Id);
RepoDevice.DeviceModel = deviceModel;
var lanMacAddresses = string.Join("; ", Request.Hardware.NetworkAdapters?.Where(na => !na.IsWlanAdapter).Select(na => na.MACAddress));
var wlanMacAddresses = string.Join("; ", Request.Hardware.NetworkAdapters?.Where(na => na.IsWlanAdapter).Select(na => na.MACAddress));
if (!string.IsNullOrEmpty(lanMacAddresses))
RepoDevice.DeviceDetails.LanMacAddress(RepoDevice, lanMacAddresses);
if (!string.IsNullOrEmpty(wlanMacAddresses))
RepoDevice.DeviceDetails.WLanMacAddress(RepoDevice, wlanMacAddresses);
if (!RepoDevice.EnrolledDate.HasValue)
RepoDevice.EnrolledDate = DateTime.Now;
RepoDevice.LastEnrolDate = DateTime.Now;
}
if (adMachineAccount == null)
{
if (RepoDevice.DeviceProfile.ProvisionADAccount)
{
EnrolmentLog.LogSessionProgress(sessionId, 50, "Provisioning an Active Directory Computer Account");
if (string.IsNullOrWhiteSpace(RepoDevice.DeviceProfile.OrganisationalUnit))
throw new InvalidOperationException("No Organisational Unit has been set in the device profile");
if (domain == null)
domain = ActiveDirectory.Context.GetDomainFromDistinguishedName(RepoDevice.DeviceProfile.OrganisationalUnit);
if (string.IsNullOrEmpty(RepoDevice.DeviceDomainId) || RepoDevice.DeviceProfile.EnforceComputerNameConvention)
RepoDevice.DeviceDomainId = RepoDevice.ComputerNameRender(Database, domain);
string offlineProvisionDiagnosicInfo;
EnrolmentLog.LogSessionTaskProvisioningADAccount(sessionId, RepoDevice.SerialNumber, RepoDevice.DeviceDomainId);
adMachineAccount = domainController.Value.RetrieveADMachineAccount(RepoDevice.DeviceDomainId);
response.OfflineDomainJoinManifest = domainController.Value.OfflineDomainJoinProvision(RepoDevice.DeviceDomainId, RepoDevice.DeviceProfile.OrganisationalUnit, ref adMachineAccount, out offlineProvisionDiagnosicInfo);
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, offlineProvisionDiagnosicInfo);
response.RequireReboot = true;
}
if (adMachineAccount != null)
{
response.ComputerName = adMachineAccount.Name;
response.DomainName = adMachineAccount.Domain.NetBiosName;
}
else if (ActiveDirectory.IsValidDomainAccountId(RepoDevice.DeviceDomainId))
{
string accountUsername;
ADDomain accountDomain;
ActiveDirectory.ParseDomainAccountId(RepoDevice.DeviceDomainId, out accountUsername, out accountDomain);
response.DomainName = accountDomain == null ? null : accountDomain.NetBiosName;
response.ComputerName = accountUsername;
}
else
{
response.DomainName = Request.DNSDomainName;
response.ComputerName = Request.ComputerName;
}
}
else
{
RepoDevice.DeviceDomainId = adMachineAccount.Id.Trim('$');
response.ComputerName = adMachineAccount.Name;
response.DomainName = adMachineAccount.Domain.NetBiosName;
// Enforce Computer Name Convention
if (!adMachineAccount.IsCriticalSystemObject && RepoDevice.DeviceProfile.EnforceComputerNameConvention)
{
if (string.IsNullOrWhiteSpace(RepoDevice.DeviceProfile.OrganisationalUnit))
throw new InvalidOperationException("No Organisational Unit has been set in the device profile");
if (domain == null)
domain = ActiveDirectory.Context.GetDomainFromDistinguishedName(RepoDevice.DeviceProfile.OrganisationalUnit);
var calculatedComputerName = RepoDevice.ComputerNameRender(Database, domain);
string calculatedAccountUsername;
ActiveDirectory.ParseDomainAccountId(calculatedComputerName, out calculatedAccountUsername);
if (!Request.ComputerName.Equals(calculatedAccountUsername, StringComparison.OrdinalIgnoreCase))
{
EnrolmentLog.LogSessionProgress(sessionId, 50, string.Format("Renaming Device: {0} -> {1}", Request.ComputerName, calculatedComputerName));
EnrolmentLog.LogSessionTaskRenamingDevice(sessionId, Request.ComputerName, calculatedComputerName);
RepoDevice.DeviceDomainId = calculatedComputerName;
response.DomainName = domain.NetBiosName;
response.ComputerName = calculatedAccountUsername;
// Create New Account
string offlineProvisionDiagnosicInfo;
response.OfflineDomainJoinManifest = domainController.Value.OfflineDomainJoinProvision(RepoDevice.DeviceDomainId, RepoDevice.DeviceProfile.OrganisationalUnit, ref adMachineAccount, out offlineProvisionDiagnosicInfo);
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, offlineProvisionDiagnosicInfo);
response.RequireReboot = true;
}
}
// Enforce Organisational Unit
if (!adMachineAccount.IsCriticalSystemObject && response.OfflineDomainJoinManifest == null && RepoDevice.DeviceProfile.EnforceOrganisationalUnit)
{
var parentDistinguishedName = adMachineAccount.ParentDistinguishedName;
if (string.IsNullOrWhiteSpace(RepoDevice.DeviceProfile.OrganisationalUnit))
throw new InvalidOperationException(string.Format("The Organisational Unit for the Device Profile '{0}' [{1}] is not set.", RepoDevice.DeviceProfile.Name, RepoDevice.DeviceProfile.Id));
if (!parentDistinguishedName.Equals(RepoDevice.DeviceProfile.OrganisationalUnit, StringComparison.OrdinalIgnoreCase)) // Custom OU
{
var proposedDomain = ActiveDirectory.Context.GetDomainFromDistinguishedName(RepoDevice.DeviceProfile.OrganisationalUnit);
var currentDomain = ActiveDirectory.Context.GetDomainFromDistinguishedName(parentDistinguishedName);
if (currentDomain != proposedDomain)
throw new NotSupportedException("Unable to move the devices organisational unit when the source and destination domains are different.");
if (domain == null)
domain = proposedDomain;
else if (domain != proposedDomain)
throw new NotSupportedException("To many domains involved in this enrolment, contact support regarding your scenario.");
EnrolmentLog.LogSessionProgress(sessionId, 65, string.Format("Moving Device Organisational Unit: {0} -> {1}", parentDistinguishedName, RepoDevice.DeviceProfile.OrganisationalUnit));
EnrolmentLog.LogSessionTaskMovingDeviceOrganisationUnit(sessionId, parentDistinguishedName, RepoDevice.DeviceProfile.OrganisationalUnit);
adMachineAccount.MoveOrganisationalUnit(domainController.Value, RepoDevice.DeviceProfile.OrganisationalUnit);
response.RequireReboot = true;
}
}
}
if (adMachineAccount != null && !adMachineAccount.IsCriticalSystemObject)
{
EnrolmentLog.LogSessionProgress(sessionId, 75, "Updating Active Directory Computer Account Properties");
// Use non-Wlan Adapter with fastest speed
var macAddress = Request.Hardware?.NetworkAdapters?.Where(na => !na.IsWlanAdapter).OrderByDescending(na => na.Speed).Select(na => na.MACAddress).FirstOrDefault();
adMachineAccount.UpdateNetbootGUID(Request.Hardware.UUID, macAddress);
if (RepoDevice.AssignedUser != null)
adMachineAccount.SetDescription(RepoDevice);
}
if (RepoDevice.DeviceProfile.DistributionType == DeviceProfile.DistributionTypes.OneToOne)
{
if (RepoDevice.AssignedUser == null)
{
response.AllowBootstrapperUninstall = false;
}
else
{
EnrolmentLog.LogSessionProgress(sessionId, 80, "Retrieving Active Directory Assigned User Account");
ADUserAccount AssignedUserInfo = ActiveDirectory.RetrieveADUserAccount(RepoDevice.AssignedUser.UserId);
EnrolmentLog.LogSessionTaskAssigningUser(sessionId, RepoDevice.SerialNumber, AssignedUserInfo.DisplayName, AssignedUserInfo.SamAccountName, AssignedUserInfo.Domain.NetBiosName, AssignedUserInfo.SecurityIdentifier.ToString());
response.AllowBootstrapperUninstall = true;
response.AssignedUserIsLocalAdmin = RepoDevice.DeviceProfile.AssignedUserLocalAdmin;
response.AssignedUserUsername = AssignedUserInfo.SamAccountName;
response.AssignedUserDomain = AssignedUserInfo.Domain.NetBiosName;
response.AssignedUserDescription = AssignedUserInfo.DisplayName;
response.AssignedUserSID = AssignedUserInfo.SecurityIdentifier.ToString();
}
}
else
{
response.AllowBootstrapperUninstall = true;
}
// Provision Certificates
if (!string.IsNullOrEmpty(RepoDevice.DeviceProfile.CertificateProviders) ||
!string.IsNullOrEmpty(RepoDevice.DeviceProfile.CertificateAuthorityProviders))
{
EnrolmentLog.LogSessionProgress(sessionId, 90, "Provisioning Certificates");
List<DeviceCertificate> provisionedCertificates;
var provisionResult = RepoDevice.ProvisionCertificates(Database, Request, out provisionedCertificates);
if (provisionedCertificates != null && provisionedCertificates.Count > 0)
{
foreach (var deviceCertificate in provisionedCertificates)
{
EnrolmentLog.LogSessionTaskProvisioningCertificate(sessionId, RepoDevice.SerialNumber, deviceCertificate.Name);
}
}
response.Certificates = provisionResult;
}
// Provision Wireless Profiles
if (!string.IsNullOrEmpty(RepoDevice.DeviceProfile.WirelessProfileProviders))
{
EnrolmentLog.LogSessionProgress(sessionId, 95, "Provisioning Wireless Profiles");
var provisionResult = RepoDevice.ProvisionWirelessProfiles(Database, Request);
if (provisionResult != null && provisionResult.Profiles != null)
{
foreach (var wirelessProfiles in provisionResult.Profiles)
{
EnrolmentLog.LogSessionTaskProvisioningCertificate(sessionId, RepoDevice.SerialNumber, wirelessProfiles.Name);
}
}
response.WirelessProfiles = provisionResult;
}
// Reset 'AllowUnauthenticatedEnrol'
if (RepoDevice.AllowUnauthenticatedEnrol)
RepoDevice.AllowUnauthenticatedEnrol = false;
EnrolmentLog.LogSessionProgress(sessionId, 100, "Completed Successfully");
}
catch (EnrolmentSafeException ex)
{
EnrolmentLog.LogSessionError(sessionId, ex);
return new EnrolResponse
{
SessionId = sessionId,
ErrorMessage = ex.Message
};
}
catch (System.Exception ex2)
{
ex2.ToExceptionless().Submit();
EnrolmentLog.LogSessionError(sessionId, ex2);
throw ex2;
}
finally
{
EnrolmentLog.LogSessionFinished(sessionId);
}
return response;
}
}
}
@@ -0,0 +1,503 @@
using Disco.Models.ClientServices;
using Disco.Services.Logging;
using Disco.Services.Logging.Models;
using System.Collections.Generic;
namespace Disco.Services.Devices.Enrolment
{
public class EnrolmentLog : LogBase
{
public enum EventTypeIds
{
SessionStarting = 10,
SessionProgress,
SessionDevice,
SessionDeviceInfo,
SessionFinished = 20,
SessionDiagnosticInformation,
SessionWarning,
SessionError,
SessionErrorWithInner,
SessionClientError,
SessionTaskAddedDevice = 50,
SessionTaskUpdatingDevice,
SessionTaskCreatedDeviceModel = 56,
SessionTaskProvisioningADAccount = 58,
SessionTaskAssigningUser = 60,
SessionTaskProvisioningCertificate = 62,
SessionTaskProvisioningWirelessProfile = 63,
SessionTaskRenamingDevice = 64,
SessionTaskMovingDeviceOrganisationUnit = 66,
ClientError = 400
}
private const int _ModuleId = 50;
public static EnrolmentLog Current
{
get
{
return (EnrolmentLog)LogContext.LogModules[50];
}
}
public override string ModuleDescription
{
get
{
return "Device Enrolment";
}
}
public override int ModuleId
{
get
{
return 50;
}
}
public override string ModuleName
{
get
{
return "DeviceEnrolment";
}
}
[System.Diagnostics.DebuggerNonUserCode]
public EnrolmentLog()
{
}
private static void Log(EnrolmentLog.EventTypeIds EventTypeId, params object[] Args)
{
EnrolmentLog.Current.Log((int)EventTypeId, Args);
}
public static void LogSessionStarting(string SessionId, string HostId, EnrolmentTypes EnrolmentType)
{
Log(EventTypeIds.SessionStarting, new object[]
{
SessionId,
HostId,
System.Enum.GetName(EnrolmentType.GetType(), EnrolmentType)
});
}
public static void LogSessionDevice(string SessionId, string DeviceSerialNumber, int? DeviceModelId)
{
Log(EventTypeIds.SessionDevice, new object[]
{
SessionId,
DeviceSerialNumber,
DeviceModelId
});
}
public static void LogSessionDeviceInfo(string SessionId, string SerialNumber, string UUID, string ComputerName, string LanMacAddress, string WlanMacAddress, string Manufacturer, string Model, string ModelType)
{
Log(EventTypeIds.SessionDeviceInfo, new object[]
{
SessionId,
SerialNumber,
UUID,
ComputerName,
LanMacAddress,
WlanMacAddress,
Manufacturer,
Model,
ModelType
});
}
public static void LogSessionDeviceInfo(string SessionId, MacEnrol Request)
{
EnrolmentLog.LogSessionDeviceInfo(SessionId, Request.DeviceSerialNumber, Request.DeviceUUID, Request.DeviceComputerName, Request.DeviceLanMacAddress, Request.DeviceWlanMacAddress, Request.DeviceManufacturer, Request.DeviceModel, Request.DeviceModelType);
}
public static void LogSessionDeviceInfo(string SessionId, Enrol Request)
{
EnrolmentLog.LogSessionDeviceInfo(SessionId, Request.SerialNumber, Request.Hardware.UUID, Request.ComputerName, null, null, Request.Hardware.Manufacturer, Request.Hardware.Model, Request.Hardware.ModelType);
}
public static void LogSessionProgress(string SessionId, int Progress, string Status)
{
Log(EventTypeIds.SessionProgress, new object[]
{
SessionId,
Progress,
Status
});
}
public static void LogSessionFinished(string SessionId)
{
Log(EventTypeIds.SessionFinished, new object[]
{
SessionId
});
}
public static void LogSessionDiagnosticInformation(string SessionId, string Message)
{
Log(EventTypeIds.SessionDiagnosticInformation, new object[]
{
SessionId,
Message
});
}
public static void LogSessionWarning(string SessionId, string Message)
{
Log(EventTypeIds.SessionWarning, new object[]
{
SessionId,
Message
});
}
public static void LogSessionError(string SessionId, System.Exception Ex)
{
if (Ex.InnerException == null)
{
Log(EventTypeIds.SessionError, new object[]
{
SessionId,
Ex.GetType().Name,
Ex.Message,
Ex.StackTrace
});
}
else
{
Log(EventTypeIds.SessionErrorWithInner, new object[]
{
SessionId,
Ex.GetType().Name,
Ex.Message,
Ex.InnerException.GetType().Name,
Ex.InnerException.Message,
Ex.StackTrace,
Ex.InnerException.StackTrace
});
}
}
public static void LogSessionClientError(string SessionId, string ClientIP, string ClientIdentifier, string ClientVersion, string Error, string RawError)
{
Log(EventTypeIds.SessionClientError, new object[]
{
SessionId,
ClientIP,
ClientIdentifier,
ClientVersion,
Error,
RawError
});
}
public static void LogSessionTaskAddedDevice(string SessionId, string DeviceSerialNumber)
{
Log(EventTypeIds.SessionTaskAddedDevice, new object[]
{
SessionId,
DeviceSerialNumber
});
}
public static void LogSessionTaskUpdatingDevice(string SessionId, string DeviceSerialNumber)
{
Log(EventTypeIds.SessionTaskUpdatingDevice, new object[]
{
SessionId,
DeviceSerialNumber
});
}
public static void LogSessionTaskCreatedDeviceModel(string SessionId, string DeviceSerialNumber, string Manufacturer, string Model)
{
Log(EventTypeIds.SessionTaskCreatedDeviceModel, new object[]
{
SessionId,
DeviceSerialNumber,
Manufacturer,
Model
});
}
public static void LogSessionTaskProvisioningADAccount(string SessionId, string DeviceSerialNumber, string ADAccountName)
{
Log(EventTypeIds.SessionTaskProvisioningADAccount, new object[]
{
SessionId,
DeviceSerialNumber,
ADAccountName
});
}
public static void LogSessionTaskAssigningUser(string SessionId, string DeviceSerialNumber, string UserDisplayName, string UserUsername, string UserDomain, string UserSID)
{
Log(EventTypeIds.SessionTaskAssigningUser, new object[]
{
SessionId,
DeviceSerialNumber,
UserDisplayName,
UserUsername,
UserDomain,
UserSID
});
}
public static void LogSessionTaskProvisioningCertificate(string SessionId, string DeviceSerialNumber, string CertificateName)
{
Log(EventTypeIds.SessionTaskProvisioningCertificate, new object[]
{
SessionId,
DeviceSerialNumber,
CertificateName
});
}
public static void LogSessionTaskProvisioningWirelessProfile(string SessionId, string DeviceSerialNumber, string WirelessProfileName)
{
Log(EventTypeIds.SessionTaskProvisioningWirelessProfile, new object[]
{
SessionId,
DeviceSerialNumber,
WirelessProfileName
});
}
public static void LogSessionTaskRenamingDevice(string SessionId, string OldComputerName, string NewComputerName)
{
Log(EventTypeIds.SessionTaskRenamingDevice, new object[]
{
SessionId,
OldComputerName,
NewComputerName
});
}
public static void LogSessionTaskMovingDeviceOrganisationUnit(string SessionId, string OldOrganisationUnit, string NewOrganisationUnit)
{
Log(EventTypeIds.SessionTaskMovingDeviceOrganisationUnit, new object[]
{
SessionId,
OldOrganisationUnit,
NewOrganisationUnit
});
}
public static void LogClientError(string ClientIP, string ClientIdentifier, string ClientVersion, string Error, string RawError)
{
Log(EventTypeIds.ClientError, new object[]
{
ClientIP,
ClientIdentifier,
ClientVersion,
Error,
RawError
});
}
protected override List<LogEventType> LoadEventTypes()
{
return new List<LogEventType>
{
new LogEventType
{
Id = (int)EventTypeIds.SessionStarting,
ModuleId = _ModuleId,
Name = "Session Starting",
Format = "Starting '{2}' Enrolment for {1} (Session# {0})",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionProgress,
ModuleId = _ModuleId,
Name = "Session Progress",
Format = "Processing Session# {0}; {1}% Complete; Status: {2}",
Severity = 0,
UseLive = true,
UsePersist = false,
UseDisplay = false
},
new LogEventType
{
Id = (int)EventTypeIds.SessionDevice,
ModuleId = _ModuleId,
Name = "Session Device",
Format = null,
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = false
},
new LogEventType
{
Id = (int)EventTypeIds.SessionDeviceInfo,
ModuleId = _ModuleId,
Name = "Session Device Info",
Format = null,
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionFinished,
ModuleId = _ModuleId,
Name = "Session Finished",
Format = "Finished Session# {0}",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionDiagnosticInformation,
ModuleId = _ModuleId,
Name = "Session Diagnostic Information",
Format = null,
Severity = 0,
UseLive = true,
UsePersist = false,
UseDisplay = false
},
new LogEventType
{
Id = (int)EventTypeIds.SessionWarning,
ModuleId = _ModuleId,
Name = "Session Warning",
Format = null,
Severity = 1,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionError,
ModuleId = _ModuleId,
Name = "Session Error",
Format = "An Error Occurred: [{1}] {2}",
Severity = 2,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionErrorWithInner,
ModuleId = _ModuleId,
Name = "Session Error with Internal",
Format = "An Error Occurred: [{1}] {2}; Internal Error: [{3}] {4}",
Severity = 2,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionClientError,
ModuleId = _ModuleId,
Name = "Client Error",
Format = "IP: {1}; Device ID: {2}; Version: {3} Error: {4}; Session# {0}",
Severity = (int)LogEventType.Severities.Error,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionTaskAddedDevice,
ModuleId = _ModuleId,
Name = "Task - Added Device",
Format = "Creating Disco Device {1}",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionTaskUpdatingDevice,
ModuleId = _ModuleId,
Name = "Task - Updating Device",
Format = "Updating Disco Device {1}",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionTaskCreatedDeviceModel,
ModuleId = _ModuleId,
Name = "Task - Creating Device Model",
Format = "Creating Device Model '{2} {3}' for Device {1}",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionTaskProvisioningADAccount,
ModuleId = _ModuleId,
Name = "Task - Provisioning Active Directory Account",
Format = "Provisioning Active Directory Account '{2}' for Device {1}",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionTaskAssigningUser,
ModuleId = _ModuleId,
Name = "Task - Assigning User",
Format = "Assigning User '{2}' ({4}\\{3} {{{5}}}) for Device {1}",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionTaskProvisioningCertificate,
ModuleId = _ModuleId,
Name = "Task - Provisioning Certificate",
Format = "Provisioning Certificate '{2}' for Device {1}",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionTaskProvisioningWirelessProfile,
ModuleId = _ModuleId,
Name = "Task - Provisioning Wireless Profile",
Format = "Provisioning Wireless Profile '{2}' for Device {1}",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionTaskRenamingDevice,
ModuleId = _ModuleId,
Name = "Task - Renaming Device",
Format = "Renaming Device '{1}' to '{2}'",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.SessionTaskMovingDeviceOrganisationUnit,
ModuleId = _ModuleId,
Name = "Task - Moving Device Organisation Unit",
Format = "Moving Device Organisation Unit '{1}' to '{2}'",
Severity = 0,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.ClientError,
ModuleId = _ModuleId,
Name = "Client Error",
Format = "IP: {0}; Device ID: {1}; Version: {2} Error: {3}",
Severity = (int)LogEventType.Severities.Error,
UseLive = true,
UsePersist = true,
UseDisplay = true
}
};
}
}
}
@@ -0,0 +1,11 @@
using System;
namespace Disco.Services.Devices.Enrolment
{
public class EnrolmentSafeException : Exception
{
public EnrolmentSafeException(string Message) : base(Message)
{
}
}
}
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Services.Devices.Enrolment
{
public enum EnrolmentTypes
{
Normal,
Mac = 5,
MacSecure,
Register = 30
}
}
@@ -0,0 +1,120 @@
using Disco.Data.Repository;
using Disco.Services.Logging;
using Disco.Services.Tasks;
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Disco.Services.Devices.Enrolment
{
public class LogMacAddressImportingTask : ScheduledTask
{
public override string TaskName { get { return "Migration: Logs to Device Mac Address Details"; } }
public override bool SingleInstanceTask { get { return true; } }
public override bool CancelInitiallySupported { get { return false; } }
#region Required Helpers
private static string RequiredFilePath(DiscoDataContext Database)
{
if (Database.DiscoConfiguration.DataStoreLocation != null)
return System.IO.Path.Combine(Database.DiscoConfiguration.DataStoreLocation, "_LogMacAddressImportingRequired.txt");
else
return null;
}
public static bool IsRequired(DiscoDataContext Database)
{
var requiredFilePath = RequiredFilePath(Database);
if (requiredFilePath == null)
return false;
else
return System.IO.File.Exists(requiredFilePath);
}
public static void SetRequired(DiscoDataContext Database)
{
var requiredFilePath = RequiredFilePath(Database);
if (requiredFilePath != null)
{
System.IO.File.WriteAllText(requiredFilePath, "This file exists to indicate an Importing of Mac Address from the Disco Logs is required. It will automatically be deleted when the import completes.");
System.IO.File.SetAttributes(requiredFilePath, System.IO.FileAttributes.Hidden);
}
// ELSE: Could never be required if no DataStoreLocation is set (no logs to process)
}
#endregion
public override void InitalizeScheduledTask(DiscoDataContext Database)
{
if (IsRequired(Database))
{
// Schedule in 15mins
var trigger = TriggerBuilder.Create()
.StartAt(DateTimeOffset.Now.AddMinutes(5));
this.ScheduleTask(trigger);
}
}
public static ScheduledTaskStatus ScheduleImmediately()
{
var existingTask = ScheduledTasks.GetTaskStatuses(typeof(LogMacAddressImportingTask)).Where(s => s.IsRunning).FirstOrDefault();
if (existingTask != null)
return existingTask;
var instance = new LogMacAddressImportingTask();
return instance.ScheduleTask();
}
protected override void ExecuteTask()
{
using (DiscoDataContext database = new DiscoDataContext())
{
Status.UpdateStatus(0, "Importing MAC Addresses", "Querying Logs for Details");
// Load Logs
var logRetriever = new ReadLogContext()
{
Module = EnrolmentLog.Current.ModuleId,
EventTypes = new List<int>() { (int)EnrolmentLog.EventTypeIds.SessionDeviceInfo }
};
var results = logRetriever.Query(database);
Status.UpdateStatus(50, string.Format("Passing {0} logs", results.Count));
Dictionary<string, Tuple<string, string>> addresses = new Dictionary<string, Tuple<string, string>>();
foreach (var result in results.OrderBy(r => r.Timestamp))
addresses[((string)result.Arguments[1]).ToLower()] = new Tuple<string, string>((string)result.Arguments[4], (string)result.Arguments[5]);
Status.UpdateStatus(75, string.Format("Importing {0} details", addresses.Count));
var devices = database.Devices.Include("DeviceDetails").ToList();
Tuple<string, string> addressResult;
foreach (var device in devices)
{
if (addresses.TryGetValue(device.SerialNumber.ToLower(), out addressResult))
{
if (!string.IsNullOrEmpty(addressResult.Item1))
device.DeviceDetails.LanMacAddress(device, addressResult.Item1);
if (!string.IsNullOrEmpty(addressResult.Item2))
device.DeviceDetails.WLanMacAddress(device, addressResult.Item2);
}
}
Status.UpdateStatus(90, "Saving to Database");
database.SaveChanges();
// Finished - Remove Placeholder File
var requiredFilePath = RequiredFilePath(database);
if (System.IO.File.Exists(requiredFilePath))
System.IO.File.Delete(requiredFilePath);
}
}
}
}
@@ -110,7 +110,7 @@ namespace Disco.Services.Devices.Exporting
}
public static DeviceExportResult GenerateExport(DiscoDataContext Database, IQueryable<Device> Devices, DeviceExportOptions Options)
{
return GenerateExport(Database, Devices, Options, ScheduledTaskMockStatus.Create());
return GenerateExport(Database, Devices, Options, ScheduledTaskMockStatus.Create("Device Export"));
}
public static DeviceExportResult GenerateExport(DiscoDataContext Database, DeviceExportOptions Options, IScheduledTaskStatus TaskStatus)
@@ -134,7 +134,7 @@ namespace Disco.Services.Devices.Exporting
}
public static DeviceExportResult GenerateExport(DiscoDataContext Database, DeviceExportOptions Options)
{
return GenerateExport(Database, Options, ScheduledTaskMockStatus.Create());
return GenerateExport(Database, Options, ScheduledTaskMockStatus.Create("Device Export"));
}
private static IEnumerable<DeviceExportRecord> BuildRecords(IQueryable<Device> Devices)