diff --git a/Disco.BI/BI/DeviceBI/Enrol.cs b/Disco.BI/BI/DeviceBI/Enrol.cs index c2eb702c..2b73bca0 100644 --- a/Disco.BI/BI/DeviceBI/Enrol.cs +++ b/Disco.BI/BI/DeviceBI/Enrol.cs @@ -5,12 +5,12 @@ using Disco.Models.Repository; using Disco.Services.Authorization; using Disco.Services.Interop.ActiveDirectory; using Disco.Services.Users; +using Exceptionless; using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using Tamir.SharpSsh; -using Exceptionless; namespace Disco.BI.DeviceBI { @@ -20,7 +20,8 @@ namespace Disco.BI.DeviceBI { Normal, Mac = 5, - MacSecure + MacSecure, + Register = 30 } private static Regex SshPromptRegEx = new Regex("[\\$,\\#]", RegexOptions.Multiline); @@ -659,5 +660,191 @@ namespace Disco.BI.DeviceBI } return response; } + + public static RegisterResponse Register(DiscoDataContext Database, string Username, Register Request) + { + RegisterResponse response = new RegisterResponse(); + ADMachineAccount adMachineAccount = null; + AuthorizationToken authenticatedToken = null; + + ADDomain domain = null; + Lazy domainController = new Lazy(() => + { + 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.DeviceSerialNumber, EnrolmentTypes.Register); + EnrolmentLog.LogSessionDeviceInfo(sessionId, Request); + + try + { + EnrolmentLog.LogSessionProgress(sessionId, 10, "Loading User Data"); + if (!string.IsNullOrWhiteSpace(Username)) + { + authenticatedToken = UserService.GetAuthorization(Username, Database); + } + EnrolmentLog.LogSessionProgress(sessionId, 13, "Loading Device Data"); + + Device RepoDevice = Database.Devices.Include("AssignedUser").Include("DeviceModel").Include("DeviceProfile").Where(d => d.SerialNumber == Request.DeviceSerialNumber).FirstOrDefault(); + EnrolmentLog.LogSessionProgress(sessionId, 15, "Discovering User/Device Disco Permissions"); + if (authenticatedToken != null) + { + if (!authenticatedToken.Has(Claims.Device.Actions.EnrolDevices)) + { + if (!authenticatedToken.Has(Claims.ComputerAccount)) + throw new EnrolSafeException(string.Format("Connection not correctly authenticated (SN: {0}; Auth User: {1})", Request.DeviceSerialNumber, authenticatedToken.User.UserId)); + + if (domain == null) + domain = ActiveDirectory.Context.GetDomainByName(Request.DeviceDNSDomainName); + + if (!authenticatedToken.User.UserId.Equals(string.Format(@"{0}\{1}$", domain.NetBiosName, Request.DeviceComputerName), System.StringComparison.OrdinalIgnoreCase)) + throw new EnrolSafeException(string.Format("Connection not correctly authenticated (SN: {0}; Auth User: {1})", Request.DeviceSerialNumber, authenticatedToken.User.UserId)); + } + } + else + { + if (RepoDevice == null) + { + throw new EnrolSafeException(string.Format("Unknown Device Serial Number (SN: '{0}')", Request.DeviceSerialNumber)); + } + 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 EnrolSafeException(string.Format("Device has no open 'Software - Reimage' job (SN: '{0}')", Request.DeviceSerialNumber)); + } + } + else + { + throw new EnrolSafeException(string.Format("Device isn't allowed an Unauthenticated Enrolment (SN: '{0}')", Request.DeviceSerialNumber)); + } + } + } + if (Request.DeviceIsPartOfDomain && !string.IsNullOrWhiteSpace(Request.DeviceComputerName)) + { + EnrolmentLog.LogSessionProgress(sessionId, 20, "Loading Active Directory Computer Account"); + System.Guid? uuidGuid = null; + if (!string.IsNullOrEmpty(Request.DeviceUUID)) + uuidGuid = ADMachineAccount.NetbootGUIDFromUUID(Request.DeviceUUID); + + if (domain == null) + domain = ActiveDirectory.Context.GetDomainByName(Request.DeviceDNSDomainName); + + var requestDeviceId = string.Format(@"{0}\{1}", domain.NetBiosName, Request.DeviceComputerName); + + adMachineAccount = domainController.Value.RetrieveADMachineAccount(requestDeviceId, uuidGuid); + } + if (RepoDevice == null) + { + EnrolmentLog.LogSessionProgress(sessionId, 30, "New Device, Creating 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); + + if (domain == null) + domain = ActiveDirectory.Context.GetDomainByName(Request.DeviceDNSDomainName); + + RepoDevice = new Device + { + SerialNumber = Request.DeviceSerialNumber, + DeviceDomainId = string.Format(@"{0}\{1}", domain.NetBiosName, Request.DeviceComputerName), + DeviceProfile = deviceProfile, + DeviceModel = deviceModel, + AllowUnauthenticatedEnrol = false, + CreatedDate = DateTime.Now, + EnrolledDate = DateTime.Now, + LastEnrolDate = DateTime.Now, + DeviceDetails = new List() + }; + Database.Devices.Add(RepoDevice); + } + else + { + EnrolmentLog.LogSessionProgress(sessionId, 30, "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; + + if (!RepoDevice.EnrolledDate.HasValue) + RepoDevice.EnrolledDate = DateTime.Now; + RepoDevice.LastEnrolDate = DateTime.Now; + } + + if (adMachineAccount == null) + { + if (adMachineAccount != null) + { + response.DeviceComputerName = adMachineAccount.Name; + response.DeviceDomainName = adMachineAccount.Domain.NetBiosName; + } + else if (ActiveDirectory.IsValidDomainAccountId(RepoDevice.DeviceDomainId)) + { + string accountUsername; + ADDomain accountDomain; + ActiveDirectory.ParseDomainAccountId(RepoDevice.DeviceDomainId, out accountUsername, out accountDomain); + + response.DeviceDomainName = accountDomain == null ? null : accountDomain.NetBiosName; + response.DeviceComputerName = accountUsername; + } + else + { + response.DeviceDomainName = Request.DeviceDNSDomainName; + response.DeviceComputerName = Request.DeviceComputerName; + } + } + else + { + RepoDevice.DeviceDomainId = adMachineAccount.Id.Trim('$'); + response.DeviceComputerName = adMachineAccount.Name; + response.DeviceDomainName = adMachineAccount.Domain.NetBiosName; + } + + // Reset 'AllowUnauthenticatedEnrol' + if (RepoDevice.AllowUnauthenticatedEnrol) + RepoDevice.AllowUnauthenticatedEnrol = false; + + EnrolmentLog.LogSessionProgress(sessionId, 100, "Completed Successfully"); + } + catch (EnrolSafeException ex) + { + EnrolmentLog.LogSessionError(sessionId, ex); + return new RegisterResponse + { + SessionId = sessionId, + ErrorMessage = ex.Message + }; + } + catch (System.Exception ex2) + { + ex2.ToExceptionless().Submit(); + EnrolmentLog.LogSessionError(sessionId, ex2); + throw ex2; + } + finally + { + EnrolmentLog.LogSessionFinished(sessionId); + } + return response; + } } } \ No newline at end of file diff --git a/Disco.BI/BI/DeviceBI/EnrolmentLog.cs b/Disco.BI/BI/DeviceBI/EnrolmentLog.cs index 1af730e7..4d805b0c 100644 --- a/Disco.BI/BI/DeviceBI/EnrolmentLog.cs +++ b/Disco.BI/BI/DeviceBI/EnrolmentLog.cs @@ -108,6 +108,10 @@ namespace Disco.BI.DeviceBI { 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, Models.ClientServices.Register Request) + { + EnrolmentLog.LogSessionDeviceInfo(SessionId, Request.DeviceSerialNumber, Request.DeviceUUID, Request.DeviceComputerName, null, null, Request.DeviceManufacturer, Request.DeviceModel, Request.DeviceModelType); + } public static void LogSessionProgress(string SessionId, int Progress, string Status) { EnrolmentLog.Log(EnrolmentLog.EventTypeIds.SessionProgress, new object[] diff --git a/Disco.BI/BI/Extensions/ClientServicesExtensions.cs b/Disco.BI/BI/Extensions/ClientServicesExtensions.cs index 954d02a3..88f3a376 100644 --- a/Disco.BI/BI/Extensions/ClientServicesExtensions.cs +++ b/Disco.BI/BI/Extensions/ClientServicesExtensions.cs @@ -1,13 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using Disco.Data.Repository; using Disco.Models.ClientServices; -using System.Web; -using Disco.Data.Repository; -using Disco.Models.Repository; -using Disco.Services.Users; using Disco.Services.Authorization; +using Disco.Services.Users; +using System; +using System.Web; namespace Disco.BI.Extensions { @@ -69,5 +65,22 @@ namespace Disco.BI.Extensions } } + public static RegisterResponse BuildResponse(this Register request) + { + if (HttpContext.Current == null) + throw new PlatformNotSupportedException("This function can only be accessed from within ASP.NET"); + + string username = null; + if (HttpContext.Current.Request.IsAuthenticated) + username = HttpContext.Current.User.Identity.Name; + + using (DiscoDataContext database = new DiscoDataContext()) + { + RegisterResponse response = DeviceBI.DeviceEnrol.Register(database, username, request); + database.SaveChanges(); + return response; + } + } + } } diff --git a/Disco.Models/ClientServices/Register.cs b/Disco.Models/ClientServices/Register.cs new file mode 100644 index 00000000..de4dee23 --- /dev/null +++ b/Disco.Models/ClientServices/Register.cs @@ -0,0 +1,26 @@ +using System.ComponentModel.DataAnnotations; + +namespace Disco.Models.ClientServices +{ + public class Register + { + [Required] + public string DeviceUUID { get; set; } + [Required] + public string DeviceSerialNumber { get; set; } + + [Required] + public string DeviceDNSDomainName { get; set; } + [Required] + public string DeviceComputerName { get; set; } + [Required] + public bool DeviceIsPartOfDomain { get; set; } + + [Required] + public string DeviceManufacturer { get; set; } + [Required] + public string DeviceModel { get; set; } + [Required] + public string DeviceModelType { get; set; } + } +} diff --git a/Disco.Models/ClientServices/RegisterResponse.cs b/Disco.Models/ClientServices/RegisterResponse.cs new file mode 100644 index 00000000..2d021a04 --- /dev/null +++ b/Disco.Models/ClientServices/RegisterResponse.cs @@ -0,0 +1,13 @@ + +namespace Disco.Models.ClientServices +{ + public class RegisterResponse + { + public string SessionId { get; set; } + + public string DeviceDomainName { get; set; } + public string DeviceComputerName { get; set; } + + public string ErrorMessage { get; set; } + } +} diff --git a/Disco.Models/Disco.Models.csproj b/Disco.Models/Disco.Models.csproj index 6ece3428..1e218837 100644 --- a/Disco.Models/Disco.Models.csproj +++ b/Disco.Models/Disco.Models.csproj @@ -46,6 +46,8 @@ + + @@ -179,7 +181,7 @@ - + diff --git a/Disco.Web/Areas/Services/Controllers/ClientController.cs b/Disco.Web/Areas/Services/Controllers/ClientController.cs index 5132e82e..dc840d1e 100644 --- a/Disco.Web/Areas/Services/Controllers/ClientController.cs +++ b/Disco.Web/Areas/Services/Controllers/ClientController.cs @@ -1,15 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using Disco.BI.Extensions; +using Disco.Data.Repository; +using Disco.Models.ClientServices; +using Disco.Services.Authorization; +using Newtonsoft.Json; +using System; using System.Web; using System.Web.Mvc; -using Disco.Models.ClientServices; -using Disco.BI; -using Disco.BI.Extensions; -using Disco.Data.Repository; using System.Web.Script.Serialization; -using Newtonsoft.Json; -using Disco.Services.Authorization; namespace Disco.Web.Areas.Services.Controllers { @@ -66,6 +63,13 @@ namespace Disco.Web.Areas.Services.Controllers return Json(enrolResponse, JsonRequestBehavior.AllowGet); } } + case "register": + { + Register registerRequest = new Register(); + this.UpdateModel(registerRequest); + RegisterResponse registerResponse = registerRequest.BuildResponse(); + return Json(registerResponse); + } } throw new MissingMethodException(string.Format("Unknown Feature: {0}", feature)); } @@ -92,6 +96,13 @@ namespace Disco.Web.Areas.Services.Controllers EnrolResponse enrolResponse = enrolRequest.BuildResponse(); return Json(enrolResponse); } + case "register": + { + Register registerRequest = new Register(); + this.UpdateModel(registerRequest); + RegisterResponse registerResponse = registerRequest.BuildResponse(); + return Json(registerResponse); + } } throw new MissingMethodException(string.Format("Unknown Feature: {0}", feature)); }