diff --git a/Disco.BI/BI/AttachmentBI/Utilities.cs b/Disco.BI/BI/AttachmentBI/Utilities.cs index 92a46502..1b28b5c1 100644 --- a/Disco.BI/BI/AttachmentBI/Utilities.cs +++ b/Disco.BI/BI/AttachmentBI/Utilities.cs @@ -15,10 +15,10 @@ namespace Disco.BI.AttachmentBI if (Source != null) { // GDI+ (jpg, png, gif, bmp) - if (SourceMimeType.Equals("image/jpeg", StringComparison.InvariantCultureIgnoreCase) || SourceMimeType.Contains("jpg") || - SourceMimeType.Equals("image/png", StringComparison.InvariantCultureIgnoreCase) || SourceMimeType.Contains("png") || - SourceMimeType.Equals("image/gif", StringComparison.InvariantCultureIgnoreCase) || SourceMimeType.Contains("gif") || - SourceMimeType.Equals("image/bmp", StringComparison.InvariantCultureIgnoreCase) || SourceMimeType.Contains("bmp")) + if (SourceMimeType.Equals("image/jpeg", StringComparison.OrdinalIgnoreCase) || SourceMimeType.Contains("jpg") || + SourceMimeType.Equals("image/png", StringComparison.OrdinalIgnoreCase) || SourceMimeType.Contains("png") || + SourceMimeType.Equals("image/gif", StringComparison.OrdinalIgnoreCase) || SourceMimeType.Contains("gif") || + SourceMimeType.Equals("image/bmp", StringComparison.OrdinalIgnoreCase) || SourceMimeType.Contains("bmp")) { try { @@ -42,7 +42,7 @@ namespace Disco.BI.AttachmentBI } // PDF - if (SourceMimeType.Equals("application/pdf", StringComparison.InvariantCultureIgnoreCase) || SourceMimeType.Contains("pdf")) + if (SourceMimeType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase) || SourceMimeType.Contains("pdf")) { PdfReader pdfReader = new PdfReader(Source); try diff --git a/Disco.BI/BI/DeviceBI/Enrol.cs b/Disco.BI/BI/DeviceBI/Enrol.cs index de26378d..c0b83200 100644 --- a/Disco.BI/BI/DeviceBI/Enrol.cs +++ b/Disco.BI/BI/DeviceBI/Enrol.cs @@ -1,7 +1,6 @@ using Disco.BI.Extensions; using Disco.Data.Repository; using Disco.Models.ClientServices; -using Disco.Models.Interop.ActiveDirectory; using Disco.Models.Repository; using Disco.Services.Authorization; using Disco.Services.Interop.ActiveDirectory; @@ -295,12 +294,12 @@ namespace Disco.BI.DeviceBI //if (RepoDeviceProfileContext.DistributionType == DeviceProfileConfiguration.DeviceProfileDistributionTypes.OneToOne && RepoDevice.AssignedUser != null) if (RepoDevice.DeviceProfile.DistributionType == DeviceProfile.DistributionTypes.OneToOne && RepoDevice.AssignedUser != null) { - ActiveDirectoryUserAccount AssignedUserInfo = Disco.Services.Interop.ActiveDirectory.ActiveDirectory.RetrieveUserAccount(RepoDevice.AssignedUser.UserId); - EnrolmentLog.LogSessionTaskAssigningUser(sessionId, RepoDevice.SerialNumber, AssignedUserInfo.DisplayName, AssignedUserInfo.SamAccountName, AssignedUserInfo.Domain, AssignedUserInfo.SecurityIdentifier); + 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; + response.DeviceAssignedUserDomain = AssignedUserInfo.Domain.NetBiosName; response.DeviceAssignedUserName = AssignedUserInfo.DisplayName; - response.DeviceAssignedUserSID = AssignedUserInfo.SecurityIdentifier; + response.DeviceAssignedUserSID = AssignedUserInfo.SecurityIdentifier.ToString(); } response.DeviceComputerName = RepoDevice.DeviceDomainId; EnrolmentLog.LogSessionProgress(sessionId, 100, "Completed Successfully"); @@ -324,23 +323,24 @@ namespace Disco.BI.DeviceBI } public static EnrolResponse Enrol(DiscoDataContext Database, string Username, Models.ClientServices.Enrol Request) { - ActiveDirectoryMachineAccount adMachineAccount = null; - + ADMachineAccount adMachineAccount = null; + EnrolResponse response = new EnrolResponse(); - + AuthorizationToken authenticatedToken = null; bool isAuthenticated = false; - ActiveDirectoryDomain domain = null; - Lazy domainController = new Lazy(() => { + ADDomain domain = null; + Lazy domainController = new Lazy(() => + { if (domain == null) throw new InvalidOperationException("The [domain] variable must be initialized first"); - return domain.RetrieveWritableDomainController(); + return domain.GetAvailableDomainController(RequireWritable: true); }); string sessionId = System.Guid.NewGuid().ToString("B"); response.SessionId = sessionId; - + EnrolmentLog.LogSessionStarting(sessionId, Request.DeviceSerialNumber, EnrolmentTypes.Normal); EnrolmentLog.LogSessionDeviceInfo(sessionId, Request); @@ -362,7 +362,7 @@ namespace Disco.BI.DeviceBI { 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 (!authenticatedToken.User.UserId.Equals(string.Format("{0}$", Request.DeviceComputerName), System.StringComparison.InvariantCultureIgnoreCase)) + if (!authenticatedToken.User.UserId.Equals(string.Format("{0}$", Request.DeviceComputerName), System.StringComparison.OrdinalIgnoreCase)) throw new EnrolSafeException(string.Format("Connection not correctly authenticated (SN: {0}; Auth User: {1})", Request.DeviceSerialNumber, authenticatedToken.User.UserId)); } } @@ -393,16 +393,16 @@ namespace Disco.BI.DeviceBI System.Guid? uuidGuid = null; System.Guid? macAddressGuid = null; if (!string.IsNullOrEmpty(Request.DeviceUUID)) - uuidGuid = ActiveDirectoryExtensions.NetbootGUIDFromUUID(Request.DeviceUUID); + uuidGuid = ADMachineAccount.NetbootGUIDFromUUID(Request.DeviceUUID); if (!string.IsNullOrEmpty(Request.DeviceLanMacAddress)) - macAddressGuid = ActiveDirectoryExtensions.NetbootGUIDFromMACAddress(Request.DeviceLanMacAddress); - + macAddressGuid = ADMachineAccount.NetbootGUIDFromMACAddress(Request.DeviceLanMacAddress); + if (domain == null) - domain = ActiveDirectory.GetDomainByDnsName(Request.DeviceDNSDomainName); + domain = ActiveDirectory.Context.GetDomainByName(Request.DeviceDNSDomainName); var requestDeviceId = string.Format(@"{0}\{1}", domain.NetBiosName, Request.DeviceComputerName); - adMachineAccount = ActiveDirectory.RetrieveMachineAccount(domainController.Value, requestDeviceId, uuidGuid, macAddressGuid); + adMachineAccount = domainController.Value.RetrieveADMachineAccount(requestDeviceId, uuidGuid, macAddressGuid); } if (RepoDevice == null) { @@ -468,20 +468,20 @@ namespace Disco.BI.DeviceBI 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.GetDomainByDistinguishedName(RepoDevice.DeviceProfile.OrganisationalUnit); + 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 = ActiveDirectory.RetrieveMachineAccount(domainController.Value, RepoDevice.DeviceDomainId); - - response.OfflineDomainJoin = ActiveDirectory.OfflineDomainJoinProvision(domain, domainController.Value, RepoDevice.DeviceDomainId, RepoDevice.DeviceProfile.OrganisationalUnit, ref adMachineAccount, out offlineProvisionDiagnosicInfo); + adMachineAccount = domainController.Value.RetrieveADMachineAccount(RepoDevice.DeviceDomainId); + + response.OfflineDomainJoin = domainController.Value.OfflineDomainJoinProvision(RepoDevice.DeviceDomainId, RepoDevice.DeviceProfile.OrganisationalUnit, ref adMachineAccount, out offlineProvisionDiagnosicInfo); EnrolmentLog.LogSessionDiagnosticInformation(sessionId, offlineProvisionDiagnosicInfo); @@ -490,7 +490,7 @@ namespace Disco.BI.DeviceBI if (adMachineAccount != null) { response.DeviceComputerName = adMachineAccount.Name; - response.DeviceDomainName = adMachineAccount.Domain; + response.DeviceDomainName = adMachineAccount.Domain.NetBiosName; } else { @@ -509,7 +509,7 @@ namespace Disco.BI.DeviceBI { RepoDevice.DeviceDomainId = adMachineAccount.Name; response.DeviceComputerName = adMachineAccount.Name; - response.DeviceDomainName = adMachineAccount.Domain; + response.DeviceDomainName = adMachineAccount.Domain.NetBiosName; // Enforce Computer Name Convention if (!adMachineAccount.IsCriticalSystemObject && RepoDevice.DeviceProfile.EnforceComputerNameConvention) @@ -517,12 +517,12 @@ namespace Disco.BI.DeviceBI if (string.IsNullOrWhiteSpace(RepoDevice.DeviceProfile.OrganisationalUnit)) throw new InvalidOperationException("No Organisational Unit has been set in the device profile"); if (domain == null) - domain = ActiveDirectory.GetDomainByDistinguishedName(RepoDevice.DeviceProfile.OrganisationalUnit); + domain = ActiveDirectory.Context.GetDomainFromDistinguishedName(RepoDevice.DeviceProfile.OrganisationalUnit); var calculatedComputerName = RepoDevice.ComputerNameRender(Database, domain); var computerNameSplit = Disco.Services.UserExtensions.SplitUserId(calculatedComputerName); - if (!Request.DeviceComputerName.Equals(computerNameSplit.Item2, StringComparison.InvariantCultureIgnoreCase)) + if (!Request.DeviceComputerName.Equals(computerNameSplit.Item2, StringComparison.OrdinalIgnoreCase)) { EnrolmentLog.LogSessionProgress(sessionId, 50, string.Format("Renaming Device: {0} -> {1}", Request.DeviceComputerName, calculatedComputerName)); EnrolmentLog.LogSessionTaskRenamingDevice(sessionId, Request.DeviceComputerName, calculatedComputerName); @@ -533,9 +533,9 @@ namespace Disco.BI.DeviceBI // Create New Account string offlineProvisionDiagnosicInfo; - - response.OfflineDomainJoin = ActiveDirectory.OfflineDomainJoinProvision(domain, domainController.Value, RepoDevice.DeviceDomainId, RepoDevice.DeviceProfile.OrganisationalUnit, ref adMachineAccount, out offlineProvisionDiagnosicInfo); - + + response.OfflineDomainJoin = domainController.Value.OfflineDomainJoinProvision(RepoDevice.DeviceDomainId, RepoDevice.DeviceProfile.OrganisationalUnit, ref adMachineAccount, out offlineProvisionDiagnosicInfo); + EnrolmentLog.LogSessionDiagnosticInformation(sessionId, offlineProvisionDiagnosicInfo); response.RequireReboot = true; @@ -545,14 +545,14 @@ namespace Disco.BI.DeviceBI // Enforce Organisational Unit if (!adMachineAccount.IsCriticalSystemObject && response.OfflineDomainJoin == null && RepoDevice.DeviceProfile.EnforceOrganisationalUnit) { - var parentDistinguishedName = adMachineAccount.ParentDistinguishedName(); + 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.InvariantCultureIgnoreCase)) // Custom OU + if (!parentDistinguishedName.Equals(RepoDevice.DeviceProfile.OrganisationalUnit, StringComparison.OrdinalIgnoreCase)) // Custom OU { - var proposedDomain = ActiveDirectory.GetDomainByDistinguishedName(RepoDevice.DeviceProfile.OrganisationalUnit); - var currentDomain = ActiveDirectory.GetDomainByDistinguishedName(parentDistinguishedName); + 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) @@ -563,7 +563,6 @@ namespace Disco.BI.DeviceBI 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); - adMachineAccount = ActiveDirectory.RetrieveMachineAccount(domainController.Value, adMachineAccount.NetBiosId); response.RequireReboot = true; } } @@ -585,14 +584,14 @@ namespace Disco.BI.DeviceBI else { EnrolmentLog.LogSessionProgress(sessionId, 80, "Retrieving Active Directory Assigned User Account"); - ActiveDirectoryUserAccount AssignedUserInfo = Services.Interop.ActiveDirectory.ActiveDirectory.RetrieveUserAccount(RepoDevice.AssignedUser.UserId); - EnrolmentLog.LogSessionTaskAssigningUser(sessionId, RepoDevice.SerialNumber, AssignedUserInfo.DisplayName, AssignedUserInfo.SamAccountName, AssignedUserInfo.Domain, AssignedUserInfo.SecurityIdentifier); + 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.DeviceAssignedUserIsLocalAdmin = RepoDevice.DeviceProfile.AssignedUserLocalAdmin; response.DeviceAssignedUserUsername = AssignedUserInfo.SamAccountName; - response.DeviceAssignedUserDomain = AssignedUserInfo.Domain; + response.DeviceAssignedUserDomain = AssignedUserInfo.Domain.NetBiosName; response.DeviceAssignedUserName = AssignedUserInfo.DisplayName; - response.DeviceAssignedUserSID = AssignedUserInfo.SecurityIdentifier; + response.DeviceAssignedUserSID = AssignedUserInfo.SecurityIdentifier.ToString(); } } else @@ -655,4 +654,4 @@ namespace Disco.BI.DeviceBI return response; } } -} +} \ No newline at end of file diff --git a/Disco.BI/BI/DocumentTemplateBI/DocumentUniqueIdentifier.cs b/Disco.BI/BI/DocumentTemplateBI/DocumentUniqueIdentifier.cs index a9380203..c209cb11 100644 --- a/Disco.BI/BI/DocumentTemplateBI/DocumentUniqueIdentifier.cs +++ b/Disco.BI/BI/DocumentTemplateBI/DocumentUniqueIdentifier.cs @@ -62,13 +62,13 @@ namespace Disco.BI.DocumentTemplateBI public string DataScope { get; private set; } public static bool IsDocumentUniqueIdentifier(string UniqueIdentifier) { - return UniqueIdentifier.StartsWith("Disco|", System.StringComparison.InvariantCultureIgnoreCase); + return UniqueIdentifier.StartsWith("Disco|", System.StringComparison.OrdinalIgnoreCase); } public DocumentUniqueIdentifier(string TemplateTypeId, string DataId, string CreatorId, DateTime TimeStamp, int? Page = null, string Tag = null) { var creatorId = (string.IsNullOrEmpty(CreatorId) || CreatorId.IndexOf('\\') > -1) ? CreatorId - : string.Format(@"{0}\{1}", ActiveDirectory.PrimaryDomain.NetBiosName, CreatorId); + : string.Format(@"{0}\{1}", ActiveDirectory.Context.PrimaryDomain.NetBiosName, CreatorId); this.Tag = Tag; this.TemplateTypeId = TemplateTypeId; @@ -100,7 +100,7 @@ namespace Disco.BI.DocumentTemplateBI { var creatorId = s[4]; if (!string.IsNullOrWhiteSpace(creatorId) && creatorId.IndexOf('\\') < 0) - creatorId = string.Format(@"{0}\{1}", ActiveDirectory.PrimaryDomain.NetBiosName, creatorId); + creatorId = string.Format(@"{0}\{1}", ActiveDirectory.Context.PrimaryDomain.NetBiosName, creatorId); this.CreatorId = creatorId; } @@ -193,7 +193,7 @@ namespace Disco.BI.DocumentTemplateBI // Patch for existing documents (before DBv13 - Multi-Domain Support) // Add default domain to User Ids if (this.DataId.IndexOf('\\') < 0) - this.DataId = string.Format(@"{0}\{1}", ActiveDirectory.PrimaryDomain.NetBiosName, this.DataId); + this.DataId = string.Format(@"{0}\{1}", ActiveDirectory.Context.PrimaryDomain.NetBiosName, this.DataId); User u = Database.Users.Find(this.DataId); if (u != null) diff --git a/Disco.BI/BI/Expressions/Extensions/ImageExt.cs b/Disco.BI/BI/Expressions/Extensions/ImageExt.cs index 45e75bab..32946551 100644 --- a/Disco.BI/BI/Expressions/Extensions/ImageExt.cs +++ b/Disco.BI/BI/Expressions/Extensions/ImageExt.cs @@ -28,7 +28,7 @@ namespace Disco.BI.Expressions.Extensions } public static FileImageExpressionResult JobAttachmentFirstImage(Job Job, DiscoDataContext Database) { - var attachment = Job.JobAttachments.FirstOrDefault(ja => ja.MimeType.StartsWith("image/", StringComparison.InvariantCultureIgnoreCase)); + var attachment = Job.JobAttachments.FirstOrDefault(ja => ja.MimeType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)); if (attachment != null) { var filename = attachment.RepositoryFilename(Database); @@ -39,7 +39,7 @@ namespace Disco.BI.Expressions.Extensions } public static FileImageExpressionResult JobAttachmentLastImage(Job Job, DiscoDataContext Database) { - var attachment = Job.JobAttachments.LastOrDefault(ja => ja.MimeType.StartsWith("image/", StringComparison.InvariantCultureIgnoreCase)); + var attachment = Job.JobAttachments.LastOrDefault(ja => ja.MimeType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)); if (attachment != null) { var filename = attachment.RepositoryFilename(Database); @@ -52,7 +52,7 @@ namespace Disco.BI.Expressions.Extensions { if (JobAttachment == null) throw new ArgumentNullException("JobAttachment"); - if (!JobAttachment.MimeType.StartsWith("image/", StringComparison.InvariantCultureIgnoreCase)) + if (!JobAttachment.MimeType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) throw new ArgumentException("Invalid Image MimeType for Attachment"); var filename = JobAttachment.RepositoryFilename(Database); @@ -65,7 +65,7 @@ namespace Disco.BI.Expressions.Extensions if (Job.JobAttachments == null) throw new ArgumentException("Job.JobAttachments is null", "Job"); - var attachments = Job.JobAttachments.Where(a => a.MimeType.StartsWith("image/", StringComparison.InvariantCultureIgnoreCase)).ToList(); + var attachments = Job.JobAttachments.Where(a => a.MimeType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)).ToList(); if (attachments.Count > 0) { @@ -81,7 +81,7 @@ namespace Disco.BI.Expressions.Extensions if (JobAttachments == null) throw new ArgumentNullException("JobAttachments"); - var attachments = JobAttachments.Cast().Where(a => a.MimeType.StartsWith("image/", StringComparison.InvariantCultureIgnoreCase)).ToList(); + var attachments = JobAttachments.Cast().Where(a => a.MimeType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)).ToList(); if (attachments.Count > 0) { diff --git a/Disco.BI/BI/Extensions/DeviceExtensions.cs b/Disco.BI/BI/Extensions/DeviceExtensions.cs index 6a41af35..0c2d6aee 100644 --- a/Disco.BI/BI/Extensions/DeviceExtensions.cs +++ b/Disco.BI/BI/Extensions/DeviceExtensions.cs @@ -6,7 +6,6 @@ using Disco.Models.Repository; using System.Collections.Generic; using System; using System.IO; -using Disco.Models.Interop.ActiveDirectory; using Disco.Services.Users; using Disco.Services.Authorization; using Disco.Services.Interop.ActiveDirectory; @@ -16,7 +15,7 @@ namespace Disco.BI.Extensions public static class DeviceExtensions { - public static string ComputerNameRender(this Device device, DiscoDataContext Database, ActiveDirectoryDomain Domain) + public static string ComputerNameRender(this Device device, DiscoDataContext Database, ADDomain Domain) { if (Domain == null) throw new ArgumentNullException("Domain"); @@ -56,12 +55,12 @@ namespace Disco.BI.Extensions public static bool UpdateLastNetworkLogonDate(this Device Device) { - return Disco.Services.Interop.ActiveDirectory.Internal.ADUpdateLastNetworkLogonDateJob.UpdateLastNetworkLogonDate(Device); + return Disco.Services.Interop.ActiveDirectory.ADTaskUpdateNetworkLogonDates.UpdateLastNetworkLogonDate(Device); } public static DeviceAttachment CreateAttachment(this Device Device, DiscoDataContext Database, User CreatorUser, string Filename, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, byte[] PdfThumbnail = null) { - if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.InvariantCultureIgnoreCase)) + if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.OrdinalIgnoreCase)) MimeType = Interop.MimeTypes.ResolveMimeType(Filename); DeviceAttachment da = new DeviceAttachment() @@ -180,7 +179,7 @@ namespace Disco.BI.Extensions // Update AD Account if (!string.IsNullOrEmpty(d.DeviceDomainId)) { - var adMachineAccount = ActiveDirectory.RetrieveMachineAccount(d.DeviceDomainId); + var adMachineAccount = ActiveDirectory.RetrieveADMachineAccount(d.DeviceDomainId); if (adMachineAccount != null) { adMachineAccount.SetDescription(d); @@ -190,10 +189,10 @@ namespace Disco.BI.Extensions return newDua; } - public static ActiveDirectoryMachineAccount ActiveDirectoryAccount(this Device Device, params string[] AdditionalProperties) + public static ADMachineAccount ActiveDirectoryAccount(this Device Device, params string[] AdditionalProperties) { if (!string.IsNullOrEmpty(Device.DeviceDomainId)) - return ActiveDirectory.RetrieveMachineAccount(Device.DeviceDomainId, AdditionalProperties: AdditionalProperties); + return ActiveDirectory.RetrieveADMachineAccount(Device.DeviceDomainId, AdditionalProperties: AdditionalProperties); else return null; } diff --git a/Disco.BI/BI/Extensions/JobExtensions.cs b/Disco.BI/BI/Extensions/JobExtensions.cs index 629e4803..505ec69a 100644 --- a/Disco.BI/BI/Extensions/JobExtensions.cs +++ b/Disco.BI/BI/Extensions/JobExtensions.cs @@ -16,7 +16,7 @@ namespace Disco.BI.Extensions { public static JobAttachment CreateAttachment(this Job Job, DiscoDataContext Database, User CreatorUser, string Filename, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, byte[] PdfThumbnail = null) { - if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.InvariantCultureIgnoreCase)) + if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.OrdinalIgnoreCase)) MimeType = Interop.MimeTypes.ResolveMimeType(Filename); JobAttachment ja = new JobAttachment() @@ -177,7 +177,7 @@ namespace Disco.BI.Extensions } foreach (var c in addedComponents) { - if (!j.JobComponents.Any(jc => jc.Description.Equals(c.Description, StringComparison.InvariantCultureIgnoreCase))) + if (!j.JobComponents.Any(jc => jc.Description.Equals(c.Description, StringComparison.OrdinalIgnoreCase))) { // Job Component with matching Description doesn't exist. Database.JobComponents.Add(new JobComponent() { diff --git a/Disco.BI/BI/Extensions/UserExtensions.cs b/Disco.BI/BI/Extensions/UserExtensions.cs index de9572af..a28707ca 100644 --- a/Disco.BI/BI/Extensions/UserExtensions.cs +++ b/Disco.BI/BI/Extensions/UserExtensions.cs @@ -6,7 +6,7 @@ using Disco.Models.Repository; using Disco.Data.Repository; using System.IO; using Disco.Models.BI.DocumentTemplates; -using Disco.Models.Interop.ActiveDirectory; +using Disco.Services.Interop.ActiveDirectory; namespace Disco.BI.Extensions { @@ -14,7 +14,7 @@ namespace Disco.BI.Extensions { public static UserAttachment CreateAttachment(this User User, DiscoDataContext Database, User CreatorUser, string Filename, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, byte[] PdfThumbnail = null) { - if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.InvariantCultureIgnoreCase)) + if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.OrdinalIgnoreCase)) MimeType = Interop.MimeTypes.ResolveMimeType(Filename); UserAttachment ua = new UserAttachment() @@ -57,9 +57,9 @@ namespace Disco.BI.Extensions { return u.DeviceUserAssignments.Where(dua => !dua.UnassignedDate.HasValue).ToList(); } - public static ActiveDirectoryUserAccount ActiveDirectoryAccount(this User User, params string[] AdditionalProperties) + public static ADUserAccount ActiveDirectoryAccount(this User User, params string[] AdditionalProperties) { - return Disco.Services.Interop.ActiveDirectory.ActiveDirectory.RetrieveUserAccount(User.UserId, AdditionalProperties); + return ActiveDirectory.RetrieveADUserAccount(User.UserId, AdditionalProperties); } public static bool CanCreateJob(this User u) diff --git a/Disco.BI/BI/Extensions/UtilityExtensions.cs b/Disco.BI/BI/Extensions/UtilityExtensions.cs index e80e740c..d6a07af7 100644 --- a/Disco.BI/BI/Extensions/UtilityExtensions.cs +++ b/Disco.BI/BI/Extensions/UtilityExtensions.cs @@ -166,7 +166,7 @@ namespace Disco.BI.Extensions } public static void SaveJpg(this Image Source, int Quality, Stream OutStream) { - ImageCodecInfo jpgCodec = ImageCodecInfo.GetImageEncoders().Where(c => c.MimeType.Equals("image/jpeg", StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); + ImageCodecInfo jpgCodec = ImageCodecInfo.GetImageEncoders().Where(c => c.MimeType.Equals("image/jpeg", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (jpgCodec != null) { if (Quality < 0 || Quality > 100) diff --git a/Disco.BI/BI/Interop/Pdf/PdfGenerator.cs b/Disco.BI/BI/Interop/Pdf/PdfGenerator.cs index be89f65b..51821bba 100644 --- a/Disco.BI/BI/Interop/Pdf/PdfGenerator.cs +++ b/Disco.BI/BI/Interop/Pdf/PdfGenerator.cs @@ -1,18 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Disco.Models.Repository; +using Disco.BI.Expressions; +using Disco.BI.Extensions; using Disco.Data.Repository; using Disco.Models.BI.DocumentTemplates; -using System.IO; -using iTextSharp.text.pdf; -using System.Collections.Concurrent; -using Disco.BI.Expressions; -using System.Collections; -using Disco.BI.Extensions; using Disco.Models.BI.Expressions; +using Disco.Models.Repository; +using Disco.Services.Interop.ActiveDirectory; using Disco.Services.Users; +using iTextSharp.text.pdf; +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; namespace Disco.BI.Interop.Pdf { @@ -67,7 +67,7 @@ namespace Disco.BI.Interop.Pdf { string dataObjectId = DataObjectsIds[idIndex]; if (!dataObjectId.Contains('\\')) - dataObjectId = Disco.Services.Interop.ActiveDirectory.ActiveDirectory.PrimaryDomain.NetBiosName + @"\" + dataObjectId; + dataObjectId = ActiveDirectory.Context.PrimaryDomain.NetBiosName + @"\" + dataObjectId; DataObjects[idIndex] = UserService.GetUser(DataObjectsIds[idIndex], Database, true); if (DataObjects[idIndex] == null) @@ -123,7 +123,7 @@ namespace Disco.BI.Interop.Pdf foreach (string pdfFieldKey in pdfStamper.AcroFields.Fields.Keys) { - if (pdfFieldKey.Equals("DiscoAttachmentId", StringComparison.InvariantCultureIgnoreCase)) + if (pdfFieldKey.Equals("DiscoAttachmentId", StringComparison.OrdinalIgnoreCase)) { AcroFields.Item fields = pdfStamper.AcroFields.Fields[pdfFieldKey]; string fieldValue = dt.UniqueIdentifier(Data, CreatorUser.UserId, TimeStamp); diff --git a/Disco.Client/Interop/LocalAuthentication.cs b/Disco.Client/Interop/LocalAuthentication.cs index 9d35969b..361d9402 100644 --- a/Disco.Client/Interop/LocalAuthentication.cs +++ b/Disco.Client/Interop/LocalAuthentication.cs @@ -22,8 +22,8 @@ namespace Disco.Client.Interop using (DirectoryEntry member = new DirectoryEntry(memberRef)) { var memberPath = member.Path; - if (memberPath.Equals(string.Format("WinNT://{0}/{1}", UserDomain, Username), StringComparison.InvariantCultureIgnoreCase) || - memberPath.Equals(string.Format("WinNT://{0}", UserSID), StringComparison.InvariantCultureIgnoreCase)) + if (memberPath.Equals(string.Format("WinNT://{0}/{1}", UserDomain, Username), StringComparison.OrdinalIgnoreCase) || + memberPath.Equals(string.Format("WinNT://{0}", UserSID), StringComparison.OrdinalIgnoreCase)) return false; } } diff --git a/Disco.Client/Interop/Network.cs b/Disco.Client/Interop/Network.cs index 6ddea1ba..8c9c6a19 100644 --- a/Disco.Client/Interop/Network.cs +++ b/Disco.Client/Interop/Network.cs @@ -34,7 +34,7 @@ namespace Disco.Client.Interop // Determine Primary Adapters // Lan - PrimaryLanNetworkAdapter = NetworkAdapters.Where(n => !n.IsWLanAdapter && n.NetConnectionId.StartsWith("Local Area Connection", StringComparison.InvariantCultureIgnoreCase)).OrderByDescending(n => n.Speed).FirstOrDefault(); + PrimaryLanNetworkAdapter = NetworkAdapters.Where(n => !n.IsWLanAdapter && n.NetConnectionId.StartsWith("Local Area Connection", StringComparison.OrdinalIgnoreCase)).OrderByDescending(n => n.Speed).FirstOrDefault(); // Might be too restrictive - remove name restriction just in case. if (PrimaryLanNetworkAdapter == null) PrimaryLanNetworkAdapter = NetworkAdapters.Where(n => !n.IsWLanAdapter).OrderByDescending(n => n.Speed).FirstOrDefault(); diff --git a/Disco.Client/Interop/SystemAudit.cs b/Disco.Client/Interop/SystemAudit.cs index e0f1b760..7190c9df 100644 --- a/Disco.Client/Interop/SystemAudit.cs +++ b/Disco.Client/Interop/SystemAudit.cs @@ -127,9 +127,9 @@ namespace Disco.Client.Interop // Added 2012-11-22 G# - Lenovo IdeaPad Serial SHIM // http://www.discoict.com.au/forum/feature-requests/2012/11/serial-number-detection-on-ideapads.aspx if (string.IsNullOrWhiteSpace(DeviceSerialNumber) || - (DeviceManufacturer.Equals("LENOVO", StringComparison.InvariantCultureIgnoreCase) && - (DeviceModel.Equals("S10-3", StringComparison.InvariantCultureIgnoreCase) // S10-3 - || DeviceModel.Equals("2957", StringComparison.InvariantCultureIgnoreCase)))) // S10-2 + (DeviceManufacturer.Equals("LENOVO", StringComparison.OrdinalIgnoreCase) && + (DeviceModel.Equals("S10-3", StringComparison.OrdinalIgnoreCase) // S10-3 + || DeviceModel.Equals("2957", StringComparison.OrdinalIgnoreCase)))) // S10-2 { try { diff --git a/Disco.ClientBootstrapper/Interop/InstallInterop.cs b/Disco.ClientBootstrapper/Interop/InstallInterop.cs index 82f34e13..279d03d9 100644 --- a/Disco.ClientBootstrapper/Interop/InstallInterop.cs +++ b/Disco.ClientBootstrapper/Interop/InstallInterop.cs @@ -74,14 +74,14 @@ namespace Disco.ClientBootstrapper.Interop // Only Copy Certain Files // Copy Wireless Certificates - if (fileName.StartsWith("WLAN_Cert_Root_", StringComparison.InvariantCultureIgnoreCase) || - fileName.StartsWith("WLAN_Cert_Intermediate_", StringComparison.InvariantCultureIgnoreCase) || - fileName.StartsWith("WLAN_Cert_Personal_", StringComparison.InvariantCultureIgnoreCase)) + if (fileName.StartsWith("WLAN_Cert_Root_", StringComparison.OrdinalIgnoreCase) || + fileName.StartsWith("WLAN_Cert_Intermediate_", StringComparison.OrdinalIgnoreCase) || + fileName.StartsWith("WLAN_Cert_Personal_", StringComparison.OrdinalIgnoreCase)) File.Copy(file, Path.Combine(InstallLocation, fileName)); // Copy Wireless Profiles - if (fileName.StartsWith("WLAN_Profile_", StringComparison.InvariantCultureIgnoreCase) && - fileName.EndsWith(".xml", StringComparison.InvariantCultureIgnoreCase)) + if (fileName.StartsWith("WLAN_Profile_", StringComparison.OrdinalIgnoreCase) && + fileName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase)) File.Copy(file, Path.Combine(InstallLocation, fileName)); } @@ -194,7 +194,7 @@ namespace Disco.ClientBootstrapper.Interop if (string.IsNullOrWhiteSpace(InstallLocation)) InstallLocation = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Disco"); - if (InstallLocation.EndsWith(".wim", StringComparison.InvariantCultureIgnoreCase)) + if (InstallLocation.EndsWith(".wim", StringComparison.OrdinalIgnoreCase)) { // Offline File System (WIM) Program.Status.UpdateStatus("Installing Bootstrapper (Offline)", "Installing", string.Format("Install Location: {0}", InstallLocation)); @@ -216,7 +216,7 @@ namespace Disco.ClientBootstrapper.Interop using (var wimImage = wim[i]) wimImageInfo.LoadXml(wimImage.ImageInformation); var wimImageInfoName = wimImageInfo.SelectSingleNode("//IMAGE/NAME"); - if (wimImageInfoName != null && wimImageInfoName.InnerText.Equals(WimImageId, StringComparison.InvariantCultureIgnoreCase)) + if (wimImageInfoName != null && wimImageInfoName.InnerText.Equals(WimImageId, StringComparison.OrdinalIgnoreCase)) { wimImageIndex = i + 1; Program.Status.UpdateStatus(null, "Analysing WIM", string.Format("Found Image Id '{0}' at Index {1}", WimImageId, wimImageIndex)); diff --git a/Disco.Data/Configuration/Modules/ActiveDirectoryConfiguration.cs b/Disco.Data/Configuration/Modules/ActiveDirectoryConfiguration.cs index 2c7b8487..43299481 100644 --- a/Disco.Data/Configuration/Modules/ActiveDirectoryConfiguration.cs +++ b/Disco.Data/Configuration/Modules/ActiveDirectoryConfiguration.cs @@ -28,7 +28,7 @@ namespace Disco.Data.Configuration.Modules } } - public bool? SearchEntireForest + public bool? SearchAllForestServers { get { return GetFromJson(null); } set { SetAsJson(value); } diff --git a/Disco.Data/Repository/DiscoDataSeeder.cs b/Disco.Data/Repository/DiscoDataSeeder.cs index 7e000a8e..4c71ced0 100644 --- a/Disco.Data/Repository/DiscoDataSeeder.cs +++ b/Disco.Data/Repository/DiscoDataSeeder.cs @@ -423,7 +423,7 @@ DELETE [Users] WHERE [Id]=@IdExisting;"; if (System.IO.Directory.Exists(userAttachmentsDirectory)) { var files = System.IO.Directory.EnumerateFiles(userAttachmentsDirectory, "*.*", System.IO.SearchOption.AllDirectories) - .Where(p => !p.StartsWith(filePrefix, StringComparison.InvariantCultureIgnoreCase) && (p.EndsWith("_thumb.jpg") || p.EndsWith("_file"))).ToList(); + .Where(p => !p.StartsWith(filePrefix, StringComparison.OrdinalIgnoreCase) && (p.EndsWith("_thumb.jpg") || p.EndsWith("_file"))).ToList(); foreach (var file in files) { diff --git a/Disco.Models/Disco.Models.csproj b/Disco.Models/Disco.Models.csproj index c80e285f..fc969ed7 100644 --- a/Disco.Models/Disco.Models.csproj +++ b/Disco.Models/Disco.Models.csproj @@ -38,7 +38,6 @@ - @@ -47,9 +46,6 @@ - - - @@ -76,10 +72,6 @@ - - - - @@ -172,7 +164,7 @@ - + diff --git a/Disco.Models/Interop/ActiveDirectory/ActiveDirectoryDomain.cs b/Disco.Models/Interop/ActiveDirectory/ActiveDirectoryDomain.cs deleted file mode 100644 index d267cf90..00000000 --- a/Disco.Models/Interop/ActiveDirectory/ActiveDirectoryDomain.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Disco.Models.Interop.ActiveDirectory -{ - public class ActiveDirectoryDomain - { - public string DnsName { get; private set; } - public string NetBiosName { get; private set; } - public string DistinguishedName { get; private set; } - - public List SearchContainers { get; private set; } - - public ActiveDirectoryDomain(string DnsName, string NetBiosName, string DistinguishedName, List SearchContainers) - { - this.DnsName = DnsName; - this.NetBiosName = NetBiosName; - this.DistinguishedName = DistinguishedName; - this.SearchContainers = SearchContainers; - } - - public void UpdateSearchContainers(IEnumerable Containers) - { - this.SearchContainers = Containers.ToList(); - } - } -} \ No newline at end of file diff --git a/Disco.Models/Interop/ActiveDirectory/ActiveDirectoryGroup.cs b/Disco.Models/Interop/ActiveDirectory/ActiveDirectoryGroup.cs deleted file mode 100644 index f922be3f..00000000 --- a/Disco.Models/Interop/ActiveDirectory/ActiveDirectoryGroup.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; - -namespace Disco.Models.Interop.ActiveDirectory -{ - public class ActiveDirectoryGroup : IActiveDirectoryObject - { - public string Domain { get; set; } - public string SamAccountName { get; set; } - - public string DistinguishedName { get; set; } - public string SecurityIdentifier { get; set; } - public string CommonName { get; set; } - - public string Name { get; set; } - public string DisplayName { get { return this.Name; } } - - public List MemberOf { get; set; } - - public string NetBiosId { get { return string.Format(@"{0}\{1}", Domain, SamAccountName); } } - } -} diff --git a/Disco.Models/Interop/ActiveDirectory/ActiveDirectoryMachineAccount.cs b/Disco.Models/Interop/ActiveDirectory/ActiveDirectoryMachineAccount.cs deleted file mode 100644 index db8df186..00000000 --- a/Disco.Models/Interop/ActiveDirectory/ActiveDirectoryMachineAccount.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Disco.Models.Repository; -using System; -using System.Collections.Generic; - -namespace Disco.Models.Interop.ActiveDirectory -{ - public class ActiveDirectoryMachineAccount : IActiveDirectoryObject - { - - public string Domain { get; set; } - public string SamAccountName { get; set; } - - public string SecurityIdentifier { get; set; } - public string DistinguishedName { get; set; } - public string Path { get; set; } - - public string Name { get; set; } - public string DisplayName { get { return this.Name; } } - public string DnsName { get; set; } - public Guid NetbootGUID { get; set; } - - public bool IsCriticalSystemObject { get; set; } - public Dictionary LoadedProperties { get; set; } - - public User ToRepositoryUser() - { - return new User - { - UserId = this.Domain + "\\" + this.SamAccountName, - DisplayName = this.Name - }; - } - - public string NetBiosId { get { return string.Format(@"{0}\{1}", Domain, SamAccountName); } } - } -} diff --git a/Disco.Models/Interop/ActiveDirectory/ActiveDirectoryOrganisationalUnit.cs b/Disco.Models/Interop/ActiveDirectory/ActiveDirectoryOrganisationalUnit.cs deleted file mode 100644 index 7cd01b77..00000000 --- a/Disco.Models/Interop/ActiveDirectory/ActiveDirectoryOrganisationalUnit.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Disco.Models.Interop.ActiveDirectory -{ - public class ActiveDirectoryOrganisationalUnit - { - public string Name { get; set; } - public string Domain { get; set; } - public string DistinguishedName { get; set; } - public List Children { get; set; } - } -} diff --git a/Disco.Models/Interop/ActiveDirectory/ActiveDirectorySearchResult.cs b/Disco.Models/Interop/ActiveDirectory/ActiveDirectorySearchResult.cs deleted file mode 100644 index 2b2be100..00000000 --- a/Disco.Models/Interop/ActiveDirectory/ActiveDirectorySearchResult.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.DirectoryServices; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Disco.Models.Interop.ActiveDirectory -{ - public class ActiveDirectorySearchResult - { - public ActiveDirectoryDomain Domain { get; set; } - public string SearchRoot { get; set; } - public SearchResult Result { get; set; } - } -} diff --git a/Disco.Models/Interop/ActiveDirectory/ActiveDirectoryUserAccount.cs b/Disco.Models/Interop/ActiveDirectory/ActiveDirectoryUserAccount.cs deleted file mode 100644 index ef4b4db4..00000000 --- a/Disco.Models/Interop/ActiveDirectory/ActiveDirectoryUserAccount.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Disco.Models.Repository; - -namespace Disco.Models.Interop.ActiveDirectory -{ - public class ActiveDirectoryUserAccount : IActiveDirectoryObject - { - public string Domain { get; set; } - public string SamAccountName { get; set; } - - public string DistinguishedName { get; set; } - public string SecurityIdentifier { get; set; } - public string Path { get; set; } - - public string Name { get; set; } - public string DisplayName { get; set; } - public string Email { get; set; } - public string Surname { get; set; } - public string GivenName { get; set; } - public string Phone { get; set; } - - public List Groups { get; set; } - - public Dictionary LoadedProperties { get; set; } - - public User ToRepositoryUser() - { - return new User - { - UserId = this.Domain + "\\" + this.SamAccountName, - DisplayName = this.DisplayName, - Surname = this.Surname, - GivenName = this.GivenName, - EmailAddress = this.Email, - PhoneNumber = this.Phone, - }; - } - - public string NetBiosId { get { return string.Format(@"{0}\{1}", Domain, SamAccountName); } } - } -} diff --git a/Disco.Models/Interop/ActiveDirectory/IActiveDirectoryObject.cs b/Disco.Models/Interop/ActiveDirectory/IActiveDirectoryObject.cs deleted file mode 100644 index 533c02ed..00000000 --- a/Disco.Models/Interop/ActiveDirectory/IActiveDirectoryObject.cs +++ /dev/null @@ -1,16 +0,0 @@ - -namespace Disco.Models.Interop.ActiveDirectory -{ - public interface IActiveDirectoryObject - { - string DistinguishedName { get; set; } - string SecurityIdentifier { get; set; } - - string Domain { get; set; } - string SamAccountName { get; set; } - string NetBiosId { get; } - - string Name { get; set; } - string DisplayName { get; } - } -} diff --git a/Disco.Models/Repository/Device/Device.cs b/Disco.Models/Repository/Device/Device.cs index f46e4224..e714c4a3 100644 --- a/Disco.Models/Repository/Device/Device.cs +++ b/Disco.Models/Repository/Device/Device.cs @@ -72,6 +72,9 @@ namespace Disco.Models.Repository { get { + if (DeviceDomainId == null) + return null; + var index = DeviceDomainId.IndexOf('\\'); return index < 0 ? DeviceDomainId : DeviceDomainId.Substring(index + 1); } @@ -82,6 +85,9 @@ namespace Disco.Models.Repository { get { + if (DeviceDomainId == null) + return null; + var index = DeviceDomainId.IndexOf('\\'); return index < 0 ? null : DeviceDomainId.Substring(0, index); } diff --git a/Disco.Models/Repository/User/User.cs b/Disco.Models/Repository/User/User.cs index 7f68fbd5..fd79cc7f 100644 --- a/Disco.Models/Repository/User/User.cs +++ b/Disco.Models/Repository/User/User.cs @@ -66,7 +66,7 @@ namespace Disco.Models.Repository public void UpdateSelf(User u) { - if (!this.UserId.Equals(u.UserId, StringComparison.InvariantCultureIgnoreCase)) + if (!this.UserId.Equals(u.UserId, StringComparison.OrdinalIgnoreCase)) throw new ArgumentException("User Id's do not match", "u"); if (this.Surname != u.Surname) diff --git a/Disco.Services/Authorization/AuthorizationToken.cs b/Disco.Services/Authorization/AuthorizationToken.cs index c0554003..c10f0a9c 100644 --- a/Disco.Services/Authorization/AuthorizationToken.cs +++ b/Disco.Services/Authorization/AuthorizationToken.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; +using Disco.Services.Interop.ActiveDirectory; namespace Disco.Services.Authorization { @@ -18,12 +19,12 @@ namespace Disco.Services.Authorization #region Token Builders - public static AuthorizationToken BuildToken(User User, List GroupMembership) + public static AuthorizationToken BuildToken(User User, IEnumerable GroupMembership) { return new AuthorizationToken() { User = User, - GroupMembership = GroupMembership, + GroupMembership = GroupMembership.ToList(), RoleTokens = RoleCache.GetRoleTokens(GroupMembership, User) }; } diff --git a/Disco.Services/Authorization/Roles/RoleCache.cs b/Disco.Services/Authorization/Roles/RoleCache.cs index c5d2cf72..23fdea52 100644 --- a/Disco.Services/Authorization/Roles/RoleCache.cs +++ b/Disco.Services/Authorization/Roles/RoleCache.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Disco.Services.Interop.ActiveDirectory; namespace Disco.Services.Authorization.Roles { @@ -49,19 +50,19 @@ namespace Disco.Services.Authorization.Roles private static IEnumerable GenerateAdministratorSubjectIds(DiscoDataContext Database) { - var domainNetBiosName = Interop.ActiveDirectory.ActiveDirectory.PrimaryDomain.NetBiosName; + var domainNetBiosName = ActiveDirectory.Context.PrimaryDomain.NetBiosName; var configuredSubjectIds = Database.DiscoConfiguration.Administrators.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Contains(@"\") ? s : string.Format(@"{0}\{1}", domainNetBiosName, s)); return RequiredAdministratorSubjectIds .Concat(configuredSubjectIds) - .Distinct(StringComparer.InvariantCultureIgnoreCase) + .Distinct(StringComparer.OrdinalIgnoreCase) .OrderBy(s => s); } public static IEnumerable RequiredAdministratorSubjectIds { get { - var domainNetBiosName = Interop.ActiveDirectory.ActiveDirectory.PrimaryDomain.NetBiosName; + var domainNetBiosName = ActiveDirectory.Context.PrimaryDomain.NetBiosName; return _RequiredAdministratorSubjectIds.Select(s => string.Format(@"{0}\{1}", domainNetBiosName, s)); } } @@ -79,7 +80,7 @@ namespace Disco.Services.Authorization.Roles SubjectIds = SubjectIds .Where(s => !string.IsNullOrWhiteSpace(s)) .Concat(RequiredAdministratorSubjectIds) - .Distinct(StringComparer.InvariantCultureIgnoreCase) + .Distinct(StringComparer.OrdinalIgnoreCase) .OrderBy(s => s); var subjectIdsString = string.Join(",", SubjectIds); @@ -136,17 +137,15 @@ namespace Disco.Services.Authorization.Roles } internal static RoleToken GetRoleToken(string SecurityGroup) { - return _Cache.FirstOrDefault(t => t.SubjectIdHashes.Contains(SecurityGroup.ToLower())); + return _Cache.FirstOrDefault(t => t.SubjectIdHashes.Contains(SecurityGroup)); } internal static List GetRoleTokens(IEnumerable SecurityGroup) { - var securityGroups = SecurityGroup.Select(sg => sg.ToLower()); - - return _Cache.Where(t => securityGroups.Any(sg => t.SubjectIdHashes.Contains(sg))).Cast().ToList(); + return _Cache.Where(t => SecurityGroup.Any(sg => t.SubjectIdHashes.Contains(sg))).Cast().ToList(); } internal static List GetRoleTokens(IEnumerable SecurityGroup, User User) { - var subjectIds = (new string[] { User.UserId }).Concat(SecurityGroup).Select(sg => sg.ToLower()); + var subjectIds = SecurityGroup.Concat(new string[] { User.UserId }); return _Cache.Where(t => subjectIds.Any(sg => t.SubjectIdHashes.Contains(sg))).Cast().ToList(); } diff --git a/Disco.Services/Authorization/Roles/RoleToken.cs b/Disco.Services/Authorization/Roles/RoleToken.cs index 036a05d3..2562de9b 100644 --- a/Disco.Services/Authorization/Roles/RoleToken.cs +++ b/Disco.Services/Authorization/Roles/RoleToken.cs @@ -30,7 +30,7 @@ namespace Disco.Services.Authorization.Roles return new RoleToken() { Role = Role, - SubjectIdHashes = new HashSet(sg.Select(i => i.ToLower())), + SubjectIdHashes = new HashSet(sg.Select(i => i.ToLower()), StringComparer.OrdinalIgnoreCase), SubjectIds = sg.ToList(), Claims = Claims }; diff --git a/Disco.Services/Disco.Services.csproj b/Disco.Services/Disco.Services.csproj index 504fce15..42f951c6 100644 --- a/Disco.Services/Disco.Services.csproj +++ b/Disco.Services/Disco.Services.csproj @@ -174,12 +174,29 @@ + + + + + + + - - - - + + + + + + + + + + + + + + @@ -286,7 +303,7 @@ - + diff --git a/Disco.Services/Interop/ActiveDirectory/ADDirectoryEntry.cs b/Disco.Services/Interop/ActiveDirectory/ADDirectoryEntry.cs new file mode 100644 index 00000000..b93f3202 --- /dev/null +++ b/Disco.Services/Interop/ActiveDirectory/ADDirectoryEntry.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.DirectoryServices; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Disco.Services.Interop.ActiveDirectory +{ + public class ADDirectoryEntry : IDisposable + { + public ADDomain Domain { get; private set; } + public ADDomainController DomainController { get; private set; } + public DirectoryEntry Entry { get; private set; } + + internal ADDirectoryEntry(ADDomain Domain, ADDomainController DomainController, DirectoryEntry Entry) + { + if (Domain == null) + throw new ArgumentNullException("Domain"); + if (DomainController == null) + throw new ArgumentNullException("DomainController"); + if (Entry == null) + throw new ArgumentNullException("Entry"); + + this.Domain = Domain; + this.DomainController = DomainController; + this.Entry = Entry; + } + + public void Dispose() + { + if (Entry != null) + { + Entry.Dispose(); + Entry = null; + } + } + + public override string ToString() + { + if (this.Entry != null) + return this.Entry.Path; + else + return base.ToString(); + } + } +} diff --git a/Disco.Services/Interop/ActiveDirectory/Internal/ADDiscoverForestServers.cs b/Disco.Services/Interop/ActiveDirectory/ADDiscoverForestServers.cs similarity index 50% rename from Disco.Services/Interop/ActiveDirectory/Internal/ADDiscoverForestServers.cs rename to Disco.Services/Interop/ActiveDirectory/ADDiscoverForestServers.cs index d0631b76..e1a5207e 100644 --- a/Disco.Services/Interop/ActiveDirectory/Internal/ADDiscoverForestServers.cs +++ b/Disco.Services/Interop/ActiveDirectory/ADDiscoverForestServers.cs @@ -7,23 +7,25 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Disco.Services.Interop.ActiveDirectory.Internal +namespace Disco.Services.Interop.ActiveDirectory { public class ADDiscoverForestServers : ScheduledTask { public override string TaskName { get { return "Active Directory - Discover Forest Servers"; } } public override bool SingleInstanceTask { get { return true; } } public override bool CancelInitiallySupported { get { return false; } } + internal static List ForestServers { get; set; } + private static object _scheduleLock = new object(); protected override void ExecuteTask() { var forestServers = DiscoverForestServers(); - ADInterop._ForestServers = forestServers; + ADDiscoverForestServers.ForestServers = forestServers; // Restrict Searching Entire Forest if to many servers using (DiscoDataContext Database = new DiscoDataContext()) { - var searchEntireForest = Database.DiscoConfiguration.ActiveDirectory.SearchEntireForest; + var searchEntireForest = Database.DiscoConfiguration.ActiveDirectory.SearchAllForestServers; // Check explicitly configured: No if (!searchEntireForest.HasValue || searchEntireForest.Value) @@ -32,12 +34,12 @@ namespace Disco.Services.Interop.ActiveDirectory.Internal if (forestServers.Count > ActiveDirectory.MaxForestServerSearch) { // Update Database - Database.DiscoConfiguration.ActiveDirectory.SearchEntireForest = false; + Database.DiscoConfiguration.ActiveDirectory.SearchAllForestServers = false; } else { // Default - Database.DiscoConfiguration.ActiveDirectory.SearchEntireForest = true; + Database.DiscoConfiguration.ActiveDirectory.SearchAllForestServers = true; } Database.SaveChanges(); @@ -52,12 +54,59 @@ namespace Disco.Services.Interop.ActiveDirectory.Internal return taskStatus; else { - var t = new ADDiscoverForestServers(); - return t.ScheduleTask(); + lock (_scheduleLock) + { + taskStatus = ScheduledTasks.GetTaskStatuses(typeof(ADDiscoverForestServers)).Where(ts => ts.IsRunning).FirstOrDefault(); + if (taskStatus != null) + return taskStatus; + else + { + var t = new ADDiscoverForestServers(); + return t.ScheduleTask(); + } + } } } - internal static List DiscoverForestServers() + public static List LoadForestServersBlocking() + { + if (ADDiscoverForestServers.ForestServers != null) + return ADDiscoverForestServers.ForestServers; + + ScheduledTaskStatus status; + lock (_scheduleLock) + { + if (ADDiscoverForestServers.ForestServers != null) + return ADDiscoverForestServers.ForestServers; + + status = ADDiscoverForestServers.ScheduleNow(); + } + + status.CompletionTask.Wait(); + return ForestServers; + } + + public static Task> LoadForestServersAsync() + { + if (ADDiscoverForestServers.ForestServers != null) + return Task.FromResult(ADDiscoverForestServers.ForestServers); + + ScheduledTaskStatus status; + lock (_scheduleLock) + { + if (ADDiscoverForestServers.ForestServers != null) + return Task.FromResult(ADDiscoverForestServers.ForestServers); + + status = ADDiscoverForestServers.ScheduleNow(); + } + + return status.CompletionTask.ContinueWith(t => + { + return ADDiscoverForestServers.ForestServers; + }); + } + + private static List DiscoverForestServers() { using (var computerDomain = Domain.GetComputerDomain()) { diff --git a/Disco.Services/Interop/ActiveDirectory/ADDomain.cs b/Disco.Services/Interop/ActiveDirectory/ADDomain.cs new file mode 100644 index 00000000..ba99bc67 --- /dev/null +++ b/Disco.Services/Interop/ActiveDirectory/ADDomain.cs @@ -0,0 +1,383 @@ +using Disco.Services.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.DirectoryServices; +using System.DirectoryServices.ActiveDirectory; +using System.Linq; +using System.Security.Principal; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Disco.Services.Interop.ActiveDirectory +{ + public class ADDomain + { + private const int DomainMaintanceIntervalMinutes = 15; + private const int SearchExceptionRetryMax = 4; + + private ActiveDirectoryContext context; + private ConcurrentStack domainControllers; + + private int domainControllerRoundRobin = -1; + private object domainMaintainLock = new object(); + private DateTime domainMaintenanceNext; + + public Domain Domain { get; private set; } + public IEnumerable DomainControllers + { + get + { + return domainControllers.ToArray(); + } + } + + public string Name { get; private set; } + public string NetBiosName { get; private set; } + public string DistinguishedName { get; private set; } + public string ConfigurationNamingContext { get; private set; } + public SecurityIdentifier SecurityIdentifier { get; private set; } + + public List SearchContainers { get; private set; } + + public ADDomain(ActiveDirectoryContext Context, Domain Domain) + { + this.context = Context; + + this.Domain = Domain; + this.SearchContainers = null; + this.domainControllers = null; + this.domainMaintenanceNext = DateTime.Now.AddMinutes(DomainMaintanceIntervalMinutes); + + this.Initialize(); + } + + private void Initialize() + { + this.Name = Domain.Name; + + var dc = Domain.FindDomainController(); + + string ldapPath = string.Format("LDAP://{0}/", dc.Name); + + using (var adRootDSE = new DirectoryEntry(ldapPath + "RootDSE")) + { + this.DistinguishedName = adRootDSE.Properties["defaultNamingContext"][0].ToString(); + this.ConfigurationNamingContext = adRootDSE.Properties["configurationNamingContext"][0].ToString(); + } + + using (var adDomainRoot = new DirectoryEntry(ldapPath + this.DistinguishedName)) + { + this.SecurityIdentifier = new SecurityIdentifier((byte[])(adDomainRoot.Properties["objectSid"][0]), 0); + } + + using (var configSearchRoot = new DirectoryEntry(ldapPath + "CN=Partitions," + this.ConfigurationNamingContext)) + { + var configSearchFilter = string.Format("(&(objectcategory=Crossref)(dnsRoot={0})(netBIOSName=*))", this.Name); + + using (var configSearcher = new DirectorySearcher(configSearchRoot, configSearchFilter, new string[] { "NetBIOSName" }, System.DirectoryServices.SearchScope.OneLevel)) + { + SearchResult configResult = configSearcher.FindOne(); + if (configResult != null) + this.NetBiosName = configResult.Properties["NetBIOSName"][0].ToString(); + else + this.NetBiosName = null; + } + } + } + + #region Domain Controllers + + public IEnumerable GetAllReachableDomainControllers() + { + return this.Domain.FindAllDomainControllers().WhereReachable().Select(dc => new ADDomainController(this.context, dc, this, dc.SiteName == this.context.Site.Name, false)); + } + + public IEnumerable GetReachableSiteDomainControllers() + { + return this.DomainControllers.Where(dc => dc.IsSiteServer && dc.DomainController.IsReachable()); + } + + public ADDomainController GetAvailableDomainController(bool RequireWritable = false) + { + if (this.domainMaintenanceNext < DateTime.Now) + MaintainDomainControllers(); + + IEnumerable availableServers; + + // Try Site Servers first + availableServers = AvilableDomainControllers(RequireSiteServer: true, RequireWritable: RequireWritable); + + if (!availableServers.Any()) + { + // No Site Servers available - try all + availableServers = AvilableDomainControllers(RequireSiteServer: false, RequireWritable: RequireWritable); + + if (!availableServers.Any()) + { + lock (domainMaintainLock) + { + availableServers = AvilableDomainControllers(RequireSiteServer: false, RequireWritable: RequireWritable); + + if (!availableServers.Any()) + return DiscoverAvailableDomainController(RequireWritable); + } + } + } + + switch (availableServers.Count()) + { + case 1: + // 1 Available DC + return availableServers.First(); + default: + // Multiple DCs Available - Round Robin + int drr = Interlocked.Increment(ref domainControllerRoundRobin); + int dcrrValue = drr % availableServers.Count(); + + if (drr > availableServers.Count()) + domainControllerRoundRobin = -1; + + return availableServers.ElementAt(dcrrValue); + } + } + private IEnumerable AvilableDomainControllers(bool RequireSiteServer, bool RequireWritable) + { + IEnumerable query = this.DomainControllers.Where(dc => dc.IsAvailable); + if (RequireSiteServer) + query = query.Where(dc => dc.IsSiteServer); + if (RequireWritable) + query = query.Where(dc => dc.IsWritable); + + return query; + } + private ADDomainController DiscoverAvailableDomainController(bool RequireWritable) + { + LocatorOptions locatorOptions; + if (RequireWritable) + locatorOptions = LocatorOptions.ForceRediscovery | LocatorOptions.WriteableRequired; + else + locatorOptions = LocatorOptions.ForceRediscovery; + + var dc = this.Domain.FindDomainController(locatorOptions); + + var dcName = dc.Name; + + var existingDC = this.DomainControllers.FirstOrDefault(edc => edc.Name == dcName); + + if (existingDC != null) + { + // DC already in scope + + // Native API indicates writable + if (RequireWritable) + existingDC.IsWritable = true; + + // Native API indicates it is available + existingDC.IsAvailable = true; + + return existingDC; + } + else + { + // New DC discovered + + var adDC = new ADDomainController(this.context, dc, this, dc.SiteName == this.context.Site.Name, RequireWritable); + + // Add DC to Available Servers + this.domainControllers.Push(adDC); + + return adDC; + } + } + + private void MaintainDomainControllers() + { + lock (domainMaintainLock) + { + var servers = this.domainControllers.ToList(); + + var nonSiteServersPresent = servers.Any(s => !s.IsSiteServer); + + if (nonSiteServersPresent) + { + var siteServersAvailable = servers.Any(s => s.IsSiteServer && s.IsAvailable); + var nonSiteServersUnavailable = servers.Any(s => !s.IsSiteServer && !s.IsAvailable); + + if (siteServersAvailable) + { + // Remove non-site servers + UpdateDomainControllers(servers.Where(s => s.IsSiteServer)); + } + else if (nonSiteServersUnavailable) + { + // Remove unavailable non-site servers + UpdateDomainControllers(servers.Where(s => s.IsSiteServer || s.IsAvailable)); + } + } + this.domainMaintenanceNext = DateTime.Now.AddMinutes(DomainMaintanceIntervalMinutes); + } + } + + internal void UpdateDomainControllers(IEnumerable DomainControllers) + { + this.domainControllers = new ConcurrentStack(DomainControllers); + } + + #endregion + + public ADDirectoryEntry RetrieveDirectoryEntry(string DistinguishedName, string[] LoadProperties = null) + { + if (string.IsNullOrWhiteSpace(DistinguishedName)) + throw new ArgumentNullException("DistinguishedName"); + + if (!DistinguishedName.EndsWith(this.DistinguishedName, StringComparison.OrdinalIgnoreCase)) + throw new ArgumentException(string.Format("The Distinguished Name ({0}) isn't a member of this domain [{1}]", DistinguishedName, this.Name), "DistinguishedName"); + + var dc = GetAvailableDomainController(); + + return dc.RetrieveDirectoryEntry(DistinguishedName, LoadProperties); + } + + #region Searching + + public IEnumerable SearchEntireDomain(string LdapFilter, string[] LoadProperties, int? ResultLimit = null) + { + return SearchInternal(this.DistinguishedName, LdapFilter, LoadProperties, ResultLimit); + } + + public IEnumerable SearchScope(string LdapFilter, string[] LoadProperties, int? ResultLimit = null) + { + var searchScope = this.SearchContainers; + + // No scope set, search entire domain + if (searchScope == null) + return SearchEntireDomain(LdapFilter, LoadProperties, ResultLimit); + + switch (searchScope.Count) + { + case 0: // Ignore domain + return Enumerable.Empty(); + + case 1: // Single-search + return SearchInternal(searchScope[0], LdapFilter, LoadProperties, ResultLimit); + + default: // Multi-search - Parallelize + + var queryTasks = searchScope.Select(scope => + Task>.Factory.StartNew(() => + SearchInternal(scope, LdapFilter, LoadProperties, ResultLimit))).ToArray(); + + // Block + Task.WaitAll(queryTasks); + + var results = queryTasks.SelectMany(t => t.Result); + if (ResultLimit.HasValue) + results = results.Take(ResultLimit.Value); + return results; + } + } + + internal IEnumerable SearchInternal(string SearchRoot, string LdapFilter, string[] LoadProperties, int? ResultLimit) + { + if (string.IsNullOrEmpty(SearchRoot)) + throw new ArgumentNullException("SearchRoot"); + if (string.IsNullOrEmpty(LdapFilter)) + throw new ArgumentNullException("LdapFilter"); + if (ResultLimit.HasValue && ResultLimit.Value < 1) + throw new ArgumentOutOfRangeException("ResultLimit", "The ResultLimit must be 1 or greater"); + + // Search with recovery + var exceptionCount = 0; + Queue exceptions = null; + do + { + var domainController = GetAvailableDomainController(); + + try + { + return domainController.SearchInternal(SearchRoot, LdapFilter, LoadProperties, ResultLimit); + } + catch (Exception ex) + { + if (exceptions == null) + exceptions = new Queue(SearchExceptionRetryMax); + + exceptions.Enqueue(ex); + exceptionCount++; + + // Set offline for DomainControllerUnavailableMinutes + domainController.IsAvailable = false; + SystemLog.LogWarning(string.Format("A domain controller [{0}] is offline. It will be retried after {1}. Error: {2} [{3}]", domainController.Name, domainController.AvailableWhen.Value.ToShortTimeString(), ex.Message, ex.GetType().Name)); + } + } while (exceptionCount < SearchExceptionRetryMax); + + throw new AggregateException( + new Exception[] { new Exception(string.Format("Unable to perform Active Directory Search after {0} attempts", exceptionCount)) } + .Concat(exceptions)); + } + + #endregion + + internal void UpdateSearchContainers(List Containers) + { + this.SearchContainers = Containers ?? new List(); + } + internal void UpdateSearchEntireDomain() + { + this.SearchContainers = null; + } + + #region Helpers + + public string DefaultComputerContainer + { + get + { + return string.Format("CN=Computers,{0}", this.DistinguishedName); + } + } + + public string FriendlyDistinguishedNamePath(string DistinguishedName) + { + if (!DistinguishedName.EndsWith(this.DistinguishedName, StringComparison.OrdinalIgnoreCase)) + throw new ArgumentException(string.Format("The Distinguished Name [{0}] doesn't exist within this domain [{1}]", DistinguishedName, this.DistinguishedName)); + + StringBuilder name = new StringBuilder(); + + name.Append('[').Append(this.NetBiosName).Append(']'); + + var subDN = DistinguishedName.Substring(0, DistinguishedName.Length - this.DistinguishedName.Length); + var subDNComponents = subDN.Split(','); + + subDNComponents + .Where(c => !string.IsNullOrWhiteSpace(c)) + .Reverse() + .Select(c => c.Substring(c.IndexOf('=') + 1)) + .ToList() + .ForEach(c => name.Append(" > ").Append(c)); + + return name.ToString(); + } + + #endregion + + public override string ToString() + { + return string.Format("{0} [{1}]", this.Name, this.NetBiosName); + } + + public override bool Equals(object obj) + { + if (obj == null || !(obj is ADDomain)) + return false; + else + return this.DistinguishedName == ((ADDomain)obj).DistinguishedName; + } + public override int GetHashCode() + { + return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this.DistinguishedName); + } + } +} \ No newline at end of file diff --git a/Disco.Services/Interop/ActiveDirectory/ADDomainController.cs b/Disco.Services/Interop/ActiveDirectory/ADDomainController.cs new file mode 100644 index 00000000..948cdc81 --- /dev/null +++ b/Disco.Services/Interop/ActiveDirectory/ADDomainController.cs @@ -0,0 +1,424 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.DirectoryServices; +using System.DirectoryServices.ActiveDirectory; +using System.Linq; +using System.Net.NetworkInformation; +using System.Security.Principal; +using System.Text; + +namespace Disco.Services.Interop.ActiveDirectory +{ + public class ADDomainController + { + private const string LdapPathTemplate = @"LDAP://{0}/{1}"; + private ActiveDirectoryContext context; + + public DomainController DomainController { get; private set; } + public ADDomain Domain { get; private set; } + public string Name { get; private set; } + public string SiteName { get; private set; } + + public bool IsSiteServer { get; private set; } + public bool IsWritable { get; internal set; } + public DateTime? AvailableWhen { get; private set; } + + public bool IsAvailable + { + get + { + var aw = AvailableWhen; + if (aw.HasValue && aw.Value < DateTime.Now) + AvailableWhen = null; + + return !aw.HasValue; + } + internal set + { + if (value) + AvailableWhen = null; + else + AvailableWhen = DateTime.Now.AddMinutes(ActiveDirectory.DomainControllerUnavailableMinutes); + } + } + + public ADDomainController(ActiveDirectoryContext Context, DomainController DomainController, ADDomain Domain, bool IsSiteServer, bool IsWritable) + { + this.context = Context; + + this.Domain = Domain; + this.DomainController = DomainController; + + this.Name = DomainController.Name; + this.SiteName = DomainController.SiteName; + + this.IsSiteServer = IsSiteServer; + this.IsWritable = IsWritable; + + this.AvailableWhen = null; + } + + public ADDirectoryEntry RetrieveDirectoryEntry(string DistinguishedName, string[] LoadProperties = null) + { + if (string.IsNullOrWhiteSpace(DistinguishedName)) + throw new ArgumentNullException("DistinguishedName"); + + if (!DistinguishedName.EndsWith(this.Domain.DistinguishedName, StringComparison.OrdinalIgnoreCase)) + throw new ArgumentException(string.Format("The Distinguished Name ({0}) isn't a member of this domain [{1}]", DistinguishedName, this.Domain.Name), "DistinguishedName"); + + var entry = new DirectoryEntry(string.Format(LdapPathTemplate, this.Name, DistinguishedName)); + + if (LoadProperties != null) + entry.RefreshCache(LoadProperties); + + return new ADDirectoryEntry(this.Domain, this, entry); + } + + #region Searching + public IEnumerable SearchEntireDomain(string LdapFilter, string[] LoadProperties, int? ResultLimit = null) + { + return SearchInternal(Domain.DistinguishedName, LdapFilter, LoadProperties, ResultLimit); + } + + public IEnumerable SearchScope(string LdapFilter, string[] LoadProperties, int? ResultLimit = null) + { + var searchScope = this.Domain.SearchContainers; + + // No scope set, search entire domain + if (searchScope == null) + return SearchEntireDomain(LdapFilter, LoadProperties, ResultLimit); + + // Ignore domain + if (searchScope.Count == 0) + return Enumerable.Empty(); + + // Multi-search + var results = searchScope.SelectMany(scope => SearchInternal(scope, LdapFilter, LoadProperties, ResultLimit)); + if (ResultLimit.HasValue) + results = results.Take(ResultLimit.Value); + return results; + } + + internal IEnumerable SearchInternal(string SearchRoot, string LdapFilter, string[] LoadProperties, int? ResultLimit) + { + if (string.IsNullOrEmpty(SearchRoot)) + throw new ArgumentNullException("SearchRoot"); + if (string.IsNullOrEmpty(LdapFilter)) + throw new ArgumentNullException("LdapFilter"); + if (ResultLimit.HasValue && ResultLimit.Value < 1) + throw new ArgumentOutOfRangeException("ResultLimit", "The ResultLimit must be 1 or greater"); + + using (ADDirectoryEntry rootEntry = this.RetrieveDirectoryEntry(SearchRoot)) + { + using (DirectorySearcher searcher = new DirectorySearcher(rootEntry.Entry, LdapFilter, LoadProperties, System.DirectoryServices.SearchScope.Subtree)) + { + searcher.PageSize = 500; + + if (ResultLimit.HasValue) + searcher.SizeLimit = ResultLimit.Value; + + return searcher.FindAll().Cast().Select(result => new ADSearchResult(Domain, this, SearchRoot, LdapFilter, result)); + } + } + } + #endregion + + #region AD Objects + + #region User Accounts + public ADUserAccount RetrieveADUserAccount(string Id, string[] AdditionalProperties = null) + { + string[] loadProperites = (AdditionalProperties != null && AdditionalProperties.Length > 0) + ? ADUserAccount.LoadProperties.Concat(AdditionalProperties).ToArray() + : ADUserAccount.LoadProperties; + + var result = RetrieveBySamAccountName(Id, ADUserAccount.LdapSamAccountNameFilterTemplate, loadProperites); + + if (result == null) + return null; + else + return result.AsADUserAccount(false, AdditionalProperties); + } + #endregion + + #region Machine Accounts + public ADMachineAccount RetrieveADMachineAccount(string Id, System.Guid? UUIDNetbootGUID, System.Guid? MacAddressNetbootGUID, string[] AdditionalProperties = null) + { + if (string.IsNullOrWhiteSpace(Id)) + throw new ArgumentNullException("Id"); + + // Add $ identifier for machine accounts + if (!Id.EndsWith("$")) + Id += "$"; + + string[] loadProperites = (AdditionalProperties != null && AdditionalProperties.Length > 0) + ? ADMachineAccount.LoadProperties.Concat(AdditionalProperties).ToArray() + : ADMachineAccount.LoadProperties; + + ADSearchResult adResult; + + adResult = RetrieveBySamAccountName(Id, ADMachineAccount.LdapSamAccountNameFilterTemplate, loadProperites); + if (adResult == null && (UUIDNetbootGUID.HasValue || MacAddressNetbootGUID.HasValue)) + { + string ldapFilter; + if (UUIDNetbootGUID.HasValue && MacAddressNetbootGUID.HasValue) + { + ldapFilter = string.Format(ADMachineAccount.LdapNetbootGuidDoubleFilterTemplate, UUIDNetbootGUID.Value.ToLdapQueryFormat(), MacAddressNetbootGUID.Value.ToLdapQueryFormat()); + } + else if (UUIDNetbootGUID.HasValue) + { + ldapFilter = string.Format(ADMachineAccount.LdapNetbootGuidSingleFilterTemplate, UUIDNetbootGUID.Value.ToLdapQueryFormat()); + } + else // MacAddressNetbootGUID.HasValue + { + ldapFilter = string.Format(ADMachineAccount.LdapNetbootGuidSingleFilterTemplate, MacAddressNetbootGUID.Value.ToLdapQueryFormat()); + } + adResult = this.SearchEntireDomain(ldapFilter, loadProperites, ActiveDirectory.SingleSearchResult).FirstOrDefault(); + } + + if (adResult != null) + return adResult.AsADMachineAccount(AdditionalProperties); + else + return null; // Not Found + } + public ADMachineAccount RetrieveADMachineAccount(string Id, string[] AdditionalProperties = null) + { + return RetrieveADMachineAccount(Id, null, null, AdditionalProperties); + } + public ADMachineAccount RetrieveADMachineAccount(string Id, System.Guid? NetbootGUID, string[] AdditionalProperties = null) + { + return RetrieveADMachineAccount(Id, NetbootGUID, null, AdditionalProperties); + } + #endregion + + #region Groups + public ADGroup RetrieveADGroup(string Id) + { + var result = RetrieveBySamAccountName(Id, ADGroup.LdapSamAccountNameFilterTemplate, ADGroup.LoadProperties); + + if (result == null) + return null; + else + return result.AsADGroup(); + } + public ADGroup RetrieveADGroupByDistinguishedName(string DistinguishedName) + { + using (var groupEntry = this.RetrieveDirectoryEntry(DistinguishedName, ADGroup.LoadProperties)) + { + if (groupEntry == null) + return null; + + return groupEntry.AsADGroup(); + } + } + public ADGroup RetrieveADGroupWithSecurityIdentifier(SecurityIdentifier SecurityIdentifier) + { + if (SecurityIdentifier == null) + throw new ArgumentNullException("SecurityIdentifier"); + if (!SecurityIdentifier.IsEqualDomainSid(this.Domain.SecurityIdentifier)) + throw new ArgumentException(string.Format("The specified Security Identifier [{0}] does not belong to this domain [{1}]", SecurityIdentifier.ToString(), this.Domain.Name), "SecurityIdentifier"); + + var sidBinaryString = SecurityIdentifier.ToBinaryString(); + + string ldapFilter = string.Format(ADGroup.LdapSecurityIdentifierFilterTemplate, sidBinaryString); + + var result = this.SearchEntireDomain(ldapFilter, ADGroup.LoadProperties, ActiveDirectory.SingleSearchResult).FirstOrDefault(); + if (result == null) + return null; + else + return result.AsADGroup(); + } + #endregion + + #region Object + private const string ObjectLdapSamAccountNameFilter = "(&(|(objectCategory=Person)(objectCategory=Computer)(objectCategory=Group))(sAMAccountName={0}))"; + private static readonly string[] ObjectLoadProperties = { "objectCategory" }; + private static readonly string[] ObjectLoadPropertiesAll = ObjectLoadProperties.Concat(ADUserAccount.LoadProperties).Concat(ADMachineAccount.LoadProperties).Concat(ADGroup.LoadProperties).Distinct().ToArray(); + + public IADObject RetrieveADObject(string Id, bool Quick) + { + var result = RetrieveBySamAccountName(Id, ObjectLdapSamAccountNameFilter, ObjectLoadPropertiesAll); + + if (result == null) + return null; + else + { + var objectCategory = result.Value("objectCategory"); + objectCategory = objectCategory.Substring(0, objectCategory.IndexOf(',')).ToLower(); + switch (objectCategory) + { + case "cn=person": + return result.AsADUserAccount(Quick, null); + case "cn=computer": + return result.AsADMachineAccount(null); + case "cn=group": + return result.AsADGroup(); + default: + throw new InvalidOperationException("Unexpected objectCategory"); + } + } + } + #endregion + + #region Organisational Units + private const string OrganisationalUnitsLdapFilter = "(objectCategory=organizationalUnit)"; + private static readonly string[] OrganisationalUnitsLoadProperties = { "name", "distinguishedName" }; + + public List RetrieveADOrganisationalUnitStructure() + { + Dictionary> resultTree = new Dictionary>(); + + var unsortedOrganisationalUnits = this.SearchEntireDomain(OrganisationalUnitsLdapFilter, OrganisationalUnitsLoadProperties) + .Select(r => r.AsADOrganisationalUnit()).ToList(); + + var indexedOrganisationalUnits = unsortedOrganisationalUnits.ToDictionary(k => k.DistinguishedName); + + var indexedChildren = unsortedOrganisationalUnits + .GroupBy(ou => ou.DistinguishedName.Substring(ou.DistinguishedName.IndexOf(',') + 1)) + .ToDictionary(g => g.Key, g => g.ToList()); + + // Link Children + foreach (var ouChildren in indexedChildren) + { + ADOrganisationalUnit ouParent; + if (indexedOrganisationalUnits.TryGetValue(ouChildren.Key, out ouParent)) + { + ouParent.Children = ouChildren.Value; + } + } + + return indexedChildren[Domain.DistinguishedName]; + } + #endregion + + private ADSearchResult RetrieveBySamAccountName(string Id, string LdapFilterTemplate, string[] LoadProperties) + { + var splitId = UserExtensions.SplitUserId(Id); + + if (!this.Domain.NetBiosName.Equals(splitId.Item1, StringComparison.OrdinalIgnoreCase)) + throw new ArgumentException(string.Format("The Id [{0}] is invalid for this domain [{1}]", Id, this.Domain.Name), "Id"); + + var ldapFilter = string.Format(LdapFilterTemplate, splitId.Item2); + + return this.SearchEntireDomain(ldapFilter, LoadProperties, ActiveDirectory.SingleSearchResult).FirstOrDefault(); + } + #endregion + + #region Actions + public bool IsReachable() + { + using (Ping p = new Ping()) + { + var pr = p.Send(this.Name, 1000); + return (pr.Status == IPStatus.Success); + } + } + + public string OfflineDomainJoinProvision(string ComputerSamAccountName, string OrganisationalUnit, ref ADMachineAccount MachineAccount, out string DiagnosticInformation) + { + if (MachineAccount != null && MachineAccount.IsCriticalSystemObject) + throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", MachineAccount.DistinguishedName)); + + if (!this.IsWritable) + throw new InvalidOperationException(string.Format("The domain controller [{0}] is not writable. This action (Offline Domain Join Provision) requires a writable domain controller.", this.Name)); + + StringBuilder diagnosticInfo = new StringBuilder(); + string DJoinResult = null; + + if (!string.IsNullOrWhiteSpace(ComputerSamAccountName)) + ComputerSamAccountName = ComputerSamAccountName.TrimEnd('$'); + if (!string.IsNullOrWhiteSpace(ComputerSamAccountName) && ComputerSamAccountName.Contains('\\')) + ComputerSamAccountName = ComputerSamAccountName.Substring(ComputerSamAccountName.IndexOf('\\') + 1); + if (string.IsNullOrWhiteSpace(ComputerSamAccountName) || ComputerSamAccountName.Length > 24) + throw new System.ArgumentException("Invalid Computer Name; > 0 and <= 24", "ComputerName"); + + // Ensure Specified OU Exists + if (!string.IsNullOrEmpty(OrganisationalUnit)) + { + try + { + using (var deOU = this.RetrieveDirectoryEntry(OrganisationalUnit, new string[] { "distinguishedName" })) + { + if (deOU == null) + throw new Exception(string.Format("OU's Directory Entry couldn't be found at [{0}]", OrganisationalUnit)); + } + } + catch (Exception ex) + { + throw new ArgumentException(string.Format("An error occurred while trying to locate the specified OU: {0}", OrganisationalUnit), "OrganisationalUnit", ex); + } + } + + if (MachineAccount != null) + MachineAccount.DeleteAccount(this); + + string tempFileName = System.IO.Path.GetTempFileName(); + string argumentOU = (!string.IsNullOrWhiteSpace(OrganisationalUnit)) ? string.Format(" /MACHINEOU \"{0}\"", OrganisationalUnit) : string.Empty; + string arguments = string.Format("/PROVISION /DOMAIN \"{0}\" /DCNAME \"{1}\" /MACHINE \"{2}\"{3} /REUSE /SAVEFILE \"{4}\"", + this.Domain.Name, + this.Name, + ComputerSamAccountName, + argumentOU, + tempFileName + ); + ProcessStartInfo commandStarter = new ProcessStartInfo("DJOIN.EXE", arguments) + { + CreateNoWindow = true, + ErrorDialog = false, + LoadUserProfile = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false + }; + diagnosticInfo.AppendFormat("{0} {1}", "DJOIN.EXE", arguments); + diagnosticInfo.AppendLine(); + + string stdOutput; + string stdError; + using (Process commandProc = Process.Start(commandStarter)) + { + commandProc.WaitForExit(20000); + stdOutput = commandProc.StandardOutput.ReadToEnd(); + stdError = commandProc.StandardError.ReadToEnd(); + } + if (!string.IsNullOrWhiteSpace(stdOutput)) + diagnosticInfo.AppendLine(stdOutput); + if (!string.IsNullOrWhiteSpace(stdError)) + diagnosticInfo.AppendLine(stdError); + + if (System.IO.File.Exists(tempFileName)) + { + DJoinResult = System.Convert.ToBase64String(System.IO.File.ReadAllBytes(tempFileName)); + System.IO.File.Delete(tempFileName); + } + if (string.IsNullOrWhiteSpace(DJoinResult)) + throw new System.InvalidOperationException(string.Format("Domain Join Unsuccessful{0}Error: {1}{0}Output: {2}", System.Environment.NewLine, stdError, stdOutput)); + + DiagnosticInformation = diagnosticInfo.ToString(); + + // Reload Machine Account + MachineAccount = this.RetrieveADMachineAccount(string.Format(@"{0}\{1}", this.Domain.NetBiosName, ComputerSamAccountName), (MachineAccount == null ? null : MachineAccount.LoadedProperties.Keys.ToArray())); + + return DJoinResult; + } + #endregion + + public override string ToString() + { + return this.Name; + } + + public override bool Equals(object obj) + { + if (obj == null || !(obj is ADDomainController)) + return false; + else + return this.Name == ((ADDomainController)obj).Name; + } + public override int GetHashCode() + { + return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this.Name); + } + } +} diff --git a/Disco.Services/Interop/ActiveDirectory/ADGroup.cs b/Disco.Services/Interop/ActiveDirectory/ADGroup.cs new file mode 100644 index 00000000..771a022f --- /dev/null +++ b/Disco.Services/Interop/ActiveDirectory/ADGroup.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.DirectoryServices; +using System.Linq; +using System.Security.Principal; + +namespace Disco.Services.Interop.ActiveDirectory +{ + public class ADGroup : IADObject + { + internal static readonly string[] LoadProperties = { "name", "distinguishedName", "sAMAccountName", "objectSid", "memberOf" }; + internal const string LdapSearchFilterTemplate = "(&(objectCategory=Group)(|(sAMAccountName=*{0}*)(name=*{0}*)(cn=*{0}*)))"; + internal const string LdapSamAccountNameFilterTemplate = "(&(objectCategory=Group)(sAMAccountName={0}))"; + internal const string LdapSecurityIdentifierFilterTemplate = "(&(objectCategory=Group)(objectSid={0}))"; + + public ADDomain Domain { get; private set; } + + public string DistinguishedName { get; private set; } + public SecurityIdentifier SecurityIdentifier { get; private set; } + + public string Id { get { return string.Format(@"{0}\{1}", Domain.NetBiosName, SamAccountName); } } + public string SamAccountName { get; private set; } + + public string Name { get; private set; } + public string DisplayName { get { return this.Name; } } + + public List MemberOf { get; private set; } + + private ADGroup(ADDomain Domain, string DistinguishedName, SecurityIdentifier SecurityIdentifier, string SamAccountName, string Name, List MemberOf) + { + this.Domain = Domain; + this.DistinguishedName = DistinguishedName; + this.SecurityIdentifier = SecurityIdentifier; + this.SamAccountName = SamAccountName; + this.Name = Name; + this.MemberOf = MemberOf; + } + + public static ADGroup FromSearchResult(ADSearchResult SearchResult) + { + if (SearchResult == null) + throw new ArgumentNullException("SearchResult"); + + var name = SearchResult.Value("name"); + var distinguishedName = SearchResult.Value("distinguishedName"); + var sAMAccountName = SearchResult.Value("sAMAccountName"); + var objectSid = new SecurityIdentifier(SearchResult.Value("objectSid"), 0); + var memberOf = SearchResult.Values("memberOf").ToList(); + + return new ADGroup(SearchResult.Domain, distinguishedName, objectSid, sAMAccountName, name, memberOf); + } + + public static ADGroup FromDirectoryEntry(ADDirectoryEntry DirectoryEntry) + { + if (DirectoryEntry == null) + throw new ArgumentNullException("DirectoryEntry"); + + var properties = DirectoryEntry.Entry.Properties; + + var name = properties.Value("name"); + var distinguishedName = properties.Value("distinguishedName"); + var sAMAccountName = properties.Value("sAMAccountName"); + var objectSid = new SecurityIdentifier(properties.Value("objectSid"), 0); + var memberOf = properties.Values("memberOf").ToList(); + + return new ADGroup(DirectoryEntry.Domain, distinguishedName, objectSid, sAMAccountName, name, memberOf); + } + + public override string ToString() + { + return this.Id; + } + + public override bool Equals(object obj) + { + if (obj == null || !(obj is ADGroup)) + return false; + else + return this.DistinguishedName == ((ADGroup)obj).DistinguishedName; + } + public override int GetHashCode() + { + return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this.DistinguishedName); + } + } +} \ No newline at end of file diff --git a/Disco.Services/Interop/ActiveDirectory/ADHelpers.cs b/Disco.Services/Interop/ActiveDirectory/ADHelpers.cs new file mode 100644 index 00000000..07b2eed5 --- /dev/null +++ b/Disco.Services/Interop/ActiveDirectory/ADHelpers.cs @@ -0,0 +1,66 @@ +using System.Security.Principal; +using System.Text; + +namespace Disco.Services.Interop.ActiveDirectory +{ + internal static class ADHelpers + { + internal static byte[] ToBytes(this SecurityIdentifier Sid) + { + var sidBytes = new byte[Sid.BinaryLength]; + + Sid.GetBinaryForm(sidBytes, 0); + + return sidBytes; + } + + internal static SecurityIdentifier BuildPrimaryGroupSid(SecurityIdentifier UserSid, int PrimaryGroupId) + { + var groupSid = UserSid.ToBytes(); + + int ridOffset = groupSid.Length - 4; + int groupId = PrimaryGroupId; + for (int i = 0; i < 4; i++) + { + groupSid[ridOffset + i] = (byte)(groupId & 0xFF); + groupId >>= 8; + } + + return new SecurityIdentifier(groupSid, 0); + } + + internal static string ToBinaryString(this SecurityIdentifier Sid) + { + StringBuilder escapedSid = new StringBuilder(); + + foreach (var sidByte in Sid.ToBytes()) + { + escapedSid.Append('\\'); + escapedSid.Append(sidByte.ToString("x2")); + } + + return escapedSid.ToString(); + } + + internal static string EscapeLdapQuery(string query) + { + return query.Replace("*", "\\2a").Replace("(", "\\28").Replace(")", "\\29").Replace("\\", "\\5c").Replace("NUL", "\\00").Replace("/", "\\2f"); + } + + internal static string ToLdapQueryFormat(this System.Guid g) + { + checked + { + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + byte[] array = g.ToByteArray(); + for (int i = 0; i < array.Length; i++) + { + byte b = array[i]; + sb.Append("\\"); + sb.Append(b.ToString("X2")); + } + return sb.ToString(); + } + } + } +} diff --git a/Disco.Services/Interop/ActiveDirectory/ADMachineAccount.cs b/Disco.Services/Interop/ActiveDirectory/ADMachineAccount.cs new file mode 100644 index 00000000..91a19efb --- /dev/null +++ b/Disco.Services/Interop/ActiveDirectory/ADMachineAccount.cs @@ -0,0 +1,361 @@ +using Disco.Models.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Principal; + +namespace Disco.Services.Interop.ActiveDirectory +{ + public class ADMachineAccount : IADObject + { + internal static readonly string[] LoadProperties = { "name", "distinguishedName", "sAMAccountName", "objectSid", "dNSHostName", "netbootGUID", "isCriticalSystemObject" }; + internal const string LdapSamAccountNameFilterTemplate = "(&(objectCategory=computer)(sAMAccountName={0}))"; + internal const string LdapNetbootGuidSingleFilterTemplate = "(&(objectCategory=computer)(netbootGUID={0}))"; + internal const string LdapNetbootGuidDoubleFilterTemplate = "(&(objectCategory=computer)(|(netbootGUID={0})(netbootGUID={1})))"; + + public ADDomain Domain { get; private set; } + + public string DistinguishedName { get; private set; } + public SecurityIdentifier SecurityIdentifier { get; private set; } + public string Id { get { return string.Format(@"{0}\{1}", Domain.NetBiosName, SamAccountName); } } + + public string SamAccountName { get; private set; } + + public string Name { get; private set; } + public string DisplayName { get { return this.Name; } } + + public string DnsName { get; private set; } + public Guid NetbootGUID { get; private set; } + + public bool IsCriticalSystemObject { get; private set; } + public Dictionary LoadedProperties { get; private set; } + + public string ParentDistinguishedName + { + get + { + return DistinguishedName.Substring(DistinguishedName.IndexOf(',') + 1); + } + } + + private ADMachineAccount(ADDomain Domain, string DistinguishedName, SecurityIdentifier SecurityIdentifier, string SamAccountName, string Name, string DnsName, Guid NetbootGUID, bool IsCriticalSystemObject, Dictionary LoadedProperties) + { + this.Domain = Domain; + this.DistinguishedName = DistinguishedName; + this.SecurityIdentifier = SecurityIdentifier; + this.SamAccountName = SamAccountName; + this.Name = Name; + this.DnsName = DnsName; + this.NetbootGUID = NetbootGUID; + this.IsCriticalSystemObject = IsCriticalSystemObject; + this.LoadedProperties = LoadedProperties; + } + + public static ADMachineAccount FromSearchResult(ADSearchResult SearchResult, string[] AdditionalProperties) + { + if (SearchResult == null) + throw new ArgumentNullException("SearchResult"); + + string name = SearchResult.Value("name"); + string sAMAccountName = SearchResult.Value("sAMAccountName"); + string distinguishedName = SearchResult.Value("distinguishedName"); + var objectSid = new SecurityIdentifier(SearchResult.Value("objectSid"), 0); + + var dNSName = SearchResult.Value("dNSHostName"); + if (dNSName == null) + dNSName = string.Format("{0}.{1}", sAMAccountName.TrimEnd('$'), SearchResult.Domain.Name); + + bool isCriticalSystemObject = SearchResult.Value("isCriticalSystemObject"); + + var netbootGUID = default(Guid); + byte[] netbootGuidBytes = SearchResult.Value("netbootGUID"); + if (netbootGuidBytes != null) + netbootGUID = new Guid(netbootGuidBytes); + + // Additional Properties + Dictionary additionalProperties; + if (AdditionalProperties != null) + additionalProperties = AdditionalProperties + .Select(p => Tuple.Create(p, SearchResult.Values(p).ToArray())) + .ToDictionary(t => t.Item1, t => t.Item2); + else + { + additionalProperties = new Dictionary(); + } + + return new ADMachineAccount( + SearchResult.Domain, + distinguishedName, + objectSid, + sAMAccountName, + name, + dNSName, + netbootGUID, + isCriticalSystemObject, + additionalProperties); + } + + public User ToRepositoryUser() + { + return new User + { + UserId = this.Id, + DisplayName = this.Name + }; + } + + public object GetPropertyValue(string PropertyName, int Index = 0) + { + switch (PropertyName.ToLower()) + { + case "name": + return this.Name; + case "samaccountname": + return this.SamAccountName; + case "distinguishedname": + return this.DistinguishedName; + case "objectsid": + return this.SecurityIdentifier.ToString(); + case "netbootguid": + return this.NetbootGUID; + default: + object[] adProperty; + if (this.LoadedProperties.TryGetValue(PropertyName, out adProperty) && Index <= adProperty.Length) + return adProperty[Index]; + else + return null; + } + } + + #region Actions + + public void DeleteAccount(ADDomainController WritableDomainController) + { + if (this.IsCriticalSystemObject) + throw new InvalidOperationException(string.Format("This account [{0}] is a Critical System Active Directory Object and Disco refuses to modify it", this.DistinguishedName)); + + if (!WritableDomainController.IsWritable) + throw new InvalidOperationException(string.Format("The domain controller [{0}] is not writable. This action (Offline Domain Join Provision) requires a writable domain controller.", this.Name)); + + using (ADDirectoryEntry entry = WritableDomainController.RetrieveDirectoryEntry(this.DistinguishedName)) + { + entry.Entry.DeleteTree(); + } + } + public void DeleteAccount() + { + this.DeleteAccount(Domain.GetAvailableDomainController(RequireWritable: true)); + } + + private void SetNetbootGUID(ADDomainController WritableDomainController, System.Guid updatedNetbootGUID) + { + if (this.IsCriticalSystemObject) + throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", this.DistinguishedName)); + + using (var deAccount = WritableDomainController.RetrieveDirectoryEntry(this.DistinguishedName)) + { + var netbootGUIDProp = deAccount.Entry.Properties["netbootGUID"]; + bool flag = netbootGUIDProp.Count > 0; + if (flag) + { + netbootGUIDProp.Clear(); + } + netbootGUIDProp.Add(updatedNetbootGUID.ToByteArray()); + deAccount.Entry.CommitChanges(); + } + } + public void SetDescription(ADDomainController WritableDomainController, string Description) + { + using (var deAccount = WritableDomainController.RetrieveDirectoryEntry(this.DistinguishedName)) + { + var descriptionProp = deAccount.Entry.Properties["description"]; + if (descriptionProp.Count > 0) + { + descriptionProp.Clear(); + } + if (!string.IsNullOrEmpty(Description)) + { + descriptionProp.Add(Description); + } + deAccount.Entry.CommitChanges(); + } + } + public void SetDescription(string Description) + { + this.SetDescription(Domain.GetAvailableDomainController(RequireWritable: true), Description); + } + + public void SetDescription(ADDomainController WritableDomainController, Device Device) + { + System.Text.StringBuilder descriptionBuilder = new System.Text.StringBuilder(); + + if (Device.AssignedUserId != null) + { + descriptionBuilder.Append(Device.AssignedUser.UserId).Append(" (").Append(Device.AssignedUser.DisplayName).Append("); "); + } + + if (Device.DeviceModelId.HasValue) + { + descriptionBuilder.Append(Device.DeviceModel.Description).Append("; "); + } + + descriptionBuilder.Append(Device.DeviceProfile.Description).Append(";"); + + string description = descriptionBuilder.ToString().Trim(); + if (description.Length > 1024) + description = description.Substring(0, 1024); + + this.SetDescription(WritableDomainController, description); + } + public void SetDescription(Device Device) + { + this.SetDescription(Domain.GetAvailableDomainController(RequireWritable: true), Device); + } + + public void DisableAccount(ADDomainController WritableDomainController) + { + if (this.IsCriticalSystemObject) + throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", this.DistinguishedName)); + + using (var deAccount = WritableDomainController.RetrieveDirectoryEntry(this.DistinguishedName)) + { + int accountControl = (int)deAccount.Entry.Properties["userAccountControl"][0]; + int updatedAccountControl = (accountControl | 2); + if (accountControl != updatedAccountControl) + { + deAccount.Entry.Properties["userAccountControl"][0] = updatedAccountControl; + deAccount.Entry.CommitChanges(); + } + } + } + public void DisableAccount() + { + this.DisableAccount(Domain.GetAvailableDomainController(RequireWritable: true)); + } + + public void EnableAccount(ADDomainController WritableDomainController) + { + if (this.IsCriticalSystemObject) + throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", this.DistinguishedName)); + + using (var deAccount = WritableDomainController.RetrieveDirectoryEntry(this.DistinguishedName)) + { + int accountControl = (int)deAccount.Entry.Properties["userAccountControl"][0]; + if ((accountControl & 2) == 2) + { + int updatedAccountControl = (accountControl ^ 2); + deAccount.Entry.Properties["userAccountControl"][0] = updatedAccountControl; + deAccount.Entry.CommitChanges(); + } + } + } + public void EnableAccount() + { + this.EnableAccount(Domain.GetAvailableDomainController(RequireWritable: true)); + } + + public bool UpdateNetbootGUID(ADDomainController WritableDomainController, string UUID, string MACAddress) + { + if (this.IsCriticalSystemObject) + throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", this.DistinguishedName)); + + Guid netbootGUID = Guid.Empty; + + if (!string.IsNullOrWhiteSpace(UUID)) + { + netbootGUID = NetbootGUIDFromUUID(UUID); + } + else if (!string.IsNullOrWhiteSpace(MACAddress)) + { + netbootGUID = NetbootGUIDFromMACAddress(MACAddress); + } + + if (netbootGUID != System.Guid.Empty && netbootGUID != this.NetbootGUID) + { + this.SetNetbootGUID(WritableDomainController, netbootGUID); + return true; + } + else + { + return false; + } + } + public void UpdateNetbootGUID(string UUID, string MACAddress) + { + this.UpdateNetbootGUID(Domain.GetAvailableDomainController(RequireWritable: true), UUID, MACAddress); + } + public static System.Guid NetbootGUIDFromUUID(string UUID) + { + return new Guid(UUID); + } + public static System.Guid NetbootGUIDFromMACAddress(string MACAddress) + { + string strippedMACAddress = MACAddress.Trim().Replace(":", string.Empty).Replace("-", string.Empty); + bool flag = strippedMACAddress.Length == 12; + System.Guid NetbootGUIDFromMACAddress; + if (flag) + { + System.Guid guid = new System.Guid(string.Format("00000000-0000-0000-0000-{0}", strippedMACAddress)); + NetbootGUIDFromMACAddress = guid; + } + else + { + NetbootGUIDFromMACAddress = System.Guid.Empty; + } + return NetbootGUIDFromMACAddress; + } + + public void MoveOrganisationalUnit(ADDomainController WritableDomainController, string NewOrganisationUnit) + { + if (this.IsCriticalSystemObject) + throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", this.DistinguishedName)); + + var parentDistinguishedName = this.ParentDistinguishedName; + + if (parentDistinguishedName != null && !parentDistinguishedName.Equals(NewOrganisationUnit, StringComparison.OrdinalIgnoreCase)) + { + // If no OU provided, place in default Computers container + if (string.IsNullOrWhiteSpace(NewOrganisationUnit)) + NewOrganisationUnit = Domain.DefaultComputerContainer; + + if (!NewOrganisationUnit.EndsWith(Domain.DistinguishedName, StringComparison.OrdinalIgnoreCase)) + throw new InvalidOperationException(string.Format("Unable to move AD Account from one domain [{0}] to another [{1}].", this.DistinguishedName, NewOrganisationUnit)); + + using (ADDirectoryEntry ou = WritableDomainController.RetrieveDirectoryEntry(NewOrganisationUnit)) + { + using (ADDirectoryEntry i = WritableDomainController.RetrieveDirectoryEntry(this.DistinguishedName)) + { + i.Entry.UsePropertyCache = false; + i.Entry.MoveTo(ou.Entry); + + // Update Distinguished Name + this.DistinguishedName = i.Entry.Properties["distinguishedName"][0].ToString(); + } + } + } + } + public void MoveOrganisationalUnit(string NewOrganisationUnit) + { + this.MoveOrganisationalUnit(Domain.GetAvailableDomainController(RequireWritable: true), NewOrganisationUnit); + } + + #endregion + + public override string ToString() + { + return this.Id; + } + + public override bool Equals(object obj) + { + if (obj == null || !(obj is ADMachineAccount)) + return false; + else + return this.DistinguishedName == ((ADMachineAccount)obj).DistinguishedName; + } + public override int GetHashCode() + { + return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this.DistinguishedName); + } + } +} diff --git a/Disco.Services/Interop/ActiveDirectory/ADOrganisationalUnit.cs b/Disco.Services/Interop/ActiveDirectory/ADOrganisationalUnit.cs new file mode 100644 index 00000000..a4ec4c67 --- /dev/null +++ b/Disco.Services/Interop/ActiveDirectory/ADOrganisationalUnit.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; + +namespace Disco.Services.Interop.ActiveDirectory +{ + public class ADOrganisationalUnit + { + public ADDomain Domain { get; private set; } + public string DistinguishedName { get; private set; } + + public string Name { get; private set; } + public List Children { get; internal set; } + + private ADOrganisationalUnit(ADDomain Domain, string DistinguishedName, string Name, List Children) + { + this.Domain = Domain; + this.DistinguishedName = DistinguishedName; + this.Name = Name; + this.Children = Children; + } + + public static ADOrganisationalUnit FromSearchResult(ADSearchResult SearchResult) + { + string distinguishedName = SearchResult.Value("distinguishedName"); + string name = SearchResult.Value("name"); + + return new ADOrganisationalUnit(SearchResult.Domain, distinguishedName, name, null); + } + + public override string ToString() + { + return this.Name; + } + + public override bool Equals(object obj) + { + if (obj == null || !(obj is ADOrganisationalUnit)) + return false; + else + return this.DistinguishedName == ((ADOrganisationalUnit)obj).DistinguishedName; + } + public override int GetHashCode() + { + return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this.DistinguishedName); + } + } +} diff --git a/Disco.Services/Interop/ActiveDirectory/ADSearchResult.cs b/Disco.Services/Interop/ActiveDirectory/ADSearchResult.cs new file mode 100644 index 00000000..f7b53823 --- /dev/null +++ b/Disco.Services/Interop/ActiveDirectory/ADSearchResult.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.DirectoryServices; +using System.Linq; + +namespace Disco.Services.Interop.ActiveDirectory +{ + public class ADSearchResult + { + private SearchResult _result; + + public ADDomain Domain { get; private set; } + public ADDomainController DomainController { get; private set; } + + public string SearchPath { get; private set; } + public string LdapFilter { get; private set; } + public string LdapPath { get; private set; } + public string DistinguishedName { get; private set; } + + public ADSearchResult(ADDomain Domain, ADDomainController DomainController, string SearchPath, string LdapFilter, SearchResult Result) + { + this._result = Result; + this.Domain = Domain; + this.DomainController = DomainController; + this.SearchPath = SearchPath; + this.LdapFilter = LdapFilter; + + this.LdapPath = _result.Path; + this.DistinguishedName = Value("dn"); + } + + public bool Contains(string PropertyName) + { + return _result.Properties.Contains(PropertyName); + } + + public T Value(string PropertyName) + { + var p = Values(PropertyName); + return p.FirstOrDefault(); + } + + public IEnumerable Values(string PropertyName) + { + var p = _result.Properties[PropertyName]; + return p.OfType(); + } + + public override string ToString() + { + return this.LdapPath; + } + } +} diff --git a/Disco.Services/Interop/ActiveDirectory/ADSite.cs b/Disco.Services/Interop/ActiveDirectory/ADSite.cs new file mode 100644 index 00000000..96216232 --- /dev/null +++ b/Disco.Services/Interop/ActiveDirectory/ADSite.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System.DirectoryServices.ActiveDirectory; +using System.Linq; + +namespace Disco.Services.Interop.ActiveDirectory +{ + public class ADSite + { + private ActiveDirectoryContext context; + + public ActiveDirectorySite Site { get; private set; } + + public string Name { get; private set; } + + public List DomainControllers { get; private set; } + + public ADSite(ActiveDirectoryContext Context, ActiveDirectorySite Site) + { + this.context = Context; + + this.Site = Site; + + this.Name = Site.Name; + this.DomainControllers = null; + } + + internal void UpdateDomainControllers(IEnumerable DomainControllers) + { + this.DomainControllers = DomainControllers.ToList(); + } + + public override string ToString() + { + return this.Name; + } + + public override bool Equals(object obj) + { + if (obj == null || !(obj is ADSite)) + return false; + else + return this.Name == ((ADSite)obj).Name; + } + public override int GetHashCode() + { + return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this.Name); + } + } +} diff --git a/Disco.Services/Interop/ActiveDirectory/ADTaskUpdateNetworkLogonDates.cs b/Disco.Services/Interop/ActiveDirectory/ADTaskUpdateNetworkLogonDates.cs new file mode 100644 index 00000000..2a368fae --- /dev/null +++ b/Disco.Services/Interop/ActiveDirectory/ADTaskUpdateNetworkLogonDates.cs @@ -0,0 +1,205 @@ +using Disco.Data.Repository; +using Disco.Models.Repository; +using Disco.Services.Logging; +using Disco.Services.Tasks; +using Quartz; +using System; +using System.Collections; +using System.Collections.Generic; +using System.DirectoryServices; +using System.DirectoryServices.ActiveDirectory; +using System.Linq; + +namespace Disco.Services.Interop.ActiveDirectory +{ + public class ADTaskUpdateNetworkLogonDates : ScheduledTask + { + + public override string TaskName { get { return "Active Directory - Update Last Network Logon Dates Task"; } } + public override bool SingleInstanceTask { get { return true; } } + public override bool CancelInitiallySupported { get { return false; } } + + public override void InitalizeScheduledTask(DiscoDataContext Database) + { + // ActiveDirectoryUpdateLastNetworkLogonDateJob @ 11:30pm + TriggerBuilder triggerBuilder = TriggerBuilder.Create(). + WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(23, 30)); + + this.ScheduleTask(triggerBuilder); + } + + protected override void ExecuteTask() + { + int changeCount; + + this.Status.UpdateStatus(1, "Starting", "Connecting to the Database and initializing the environment"); + using (DiscoDataContext database = new DiscoDataContext()) + { + UpdateLastNetworkLogonDates(database, this.Status); + this.Status.UpdateStatus(95, "Updating Database", "Writing last network logon dates to the Database"); + changeCount = database.SaveChanges(); + this.Status.Finished(string.Format("{0} Device last network logon dates updated", changeCount), "/Config/SystemConfig"); + } + + SystemLog.LogInformation(new string[] + { + "Updated LastNetworkLogon Device Property for Device/s", + changeCount.ToString() + }); + } + + public static ScheduledTaskStatus ScheduleImmediately() + { + var existingTask = ScheduledTasks.GetTaskStatuses(typeof(ADTaskUpdateNetworkLogonDates)).Where(s => s.IsRunning).FirstOrDefault(); + if (existingTask != null) + return existingTask; + + var instance = new ADTaskUpdateNetworkLogonDates(); + return instance.ScheduleTask(); + } + + public static bool UpdateLastNetworkLogonDate(Device Device) + { + const string ldapFilterTemplate = "(&(objectCategory=Computer)(sAMAccountName={0}))"; + string[] ldapProperties = new string[] { "lastLogon", "lastLogonTimestamp" }; + + System.DateTime? lastLogon = null; + + if (!string.IsNullOrEmpty(Device.DeviceDomainId)) + { + var context = ActiveDirectory.Context; + var deviceSamAccountName = UserExtensions.SplitUserId(Device.DeviceDomainId).Item2 + "$"; + var ldapFilter = string.Format(ldapFilterTemplate, ADHelpers.EscapeLdapQuery(deviceSamAccountName)); + + var domain = context.GetDomainFromId(Device.DeviceDomainId); + IEnumerable domainControllers; + + if (context.SearchAllForestServers) + domainControllers = domain.GetAllReachableDomainControllers(); + else + domainControllers = domain.GetReachableSiteDomainControllers(); + + lastLogon = domainControllers.Select(dc => + { + var result = dc.SearchEntireDomain(ldapFilter, ldapProperties, ActiveDirectory.SingleSearchResult).FirstOrDefault(); + + if (result != null) + { + long lastLogonValue = default(long); + long lastLogonTimestampValue = default(long); + + lastLogonValue = result.Value("lastLogon"); + lastLogonTimestampValue = result.Value("lastLogonTimestamp"); + + long highedValue = Math.Max(lastLogonValue, lastLogonTimestampValue); + + if (highedValue > 0) + return (DateTime?)new DateTime((DateTime.FromFileTime(highedValue).Ticks / 10000000L) * 10000000L); + else + return null; + } + return null; + }).Where(dt => dt.HasValue).Max(); + } + + if (lastLogon.HasValue && + ( + !Device.LastNetworkLogonDate.HasValue + || Device.LastNetworkLogonDate.Value < lastLogon + )) + { + Device.LastNetworkLogonDate = lastLogon; + return true; + } + return false; + } + + private static void UpdateLastNetworkLogonDates(DiscoDataContext Database, ScheduledTaskStatus status) + { + var context = ActiveDirectory.Context; + const string ldapFilter = "(objectCategory=Computer)"; + string[] ldapProperties = new string[] { "sAMAccountName", "lastLogon" }; + + status.UpdateStatus(2, "Initializing", "Determining Domains and Available Domain Controllers"); + + // Determine Domain Scopes to Query + var domainQueries = context.Domains + .Select(d => Tuple.Create(d, d.SearchContainers ?? new List() { d.DistinguishedName })) + .Where(d => d.Item2.Count > 0); + + // Determine Domain Controllers to Query + IEnumerable>> serverQueries; + if (context.SearchAllForestServers) + serverQueries = domainQueries.SelectMany(q => q.Item1.GetAllReachableDomainControllers(), (q, dc) => Tuple.Create(q.Item1, dc, q.Item2)); + else + serverQueries = domainQueries.SelectMany(q => q.Item1.GetReachableSiteDomainControllers(), (q, dc) => Tuple.Create(q.Item1, dc, q.Item2)); + + var scopedQueries = serverQueries.SelectMany(q => q.Item3, (q, scope) => Tuple.Create(q.Item1, q.Item2, scope)).ToList(); + + var queries = Enumerable.Range(0, scopedQueries.Count).Select(i => + { + var q = scopedQueries[i]; + return Tuple.Create(i, q.Item1, q.Item2, q.Item3); + }); + + var queryResults = queries.SelectMany(q => + { + var queryIndex = q.Item1; + var domain = q.Item2; + var domainController = q.Item3; + var searchRoot = q.Item4; + + // Update Status + double progress = 5 + (queryIndex * (90 / scopedQueries.Count)); + status.UpdateStatus(progress, string.Format("Querying Domain [{0}] using controller [{1}]", domain.NetBiosName, domainController.Name), string.Format("Searching: {0}", searchRoot)); + + // Perform Query + var directoryResults = domainController.SearchInternal(searchRoot, ldapFilter, ldapProperties, null); + + return directoryResults.Select(result => + { + var samAccountName = result.Value("sAMAccountName"); + + long lastLogonValue = default(long); + long lastLogonTimestampValue = default(long); + + lastLogonValue = result.Value("lastLogon"); + lastLogonTimestampValue = result.Value("lastLogonTimestamp"); + + long highedValue = Math.Max(lastLogonValue, lastLogonTimestampValue); + + if (highedValue > 0) + { + var computerName = string.Format(@"{0}\{1}", domain.NetBiosName, samAccountName.TrimEnd('$')); + var lastLogon = new DateTime((DateTime.FromFileTime(highedValue).Ticks / 10000000L) * 10000000L); + return Tuple.Create(computerName, lastLogon); + } + else + return null; + }).Where(i => i != null).ToList(); + + }).GroupBy(r => r.Item1, StringComparer.OrdinalIgnoreCase).ToDictionary(g => g.Key.ToUpper(), g => g.Max(i => i.Item2)); + + status.UpdateStatus(90, "Processing Results", "Processing last network logon dates and looking for updates"); + + foreach (Device device in Database.Devices.Where(device => device.DeviceDomainId != null)) + { + DateTime lastLogonDate; + if (queryResults.TryGetValue(device.DeviceDomainId.ToUpper(), out lastLogonDate)) + { + if (!device.LastNetworkLogonDate.HasValue) + device.LastNetworkLogonDate = lastLogonDate; + else + { + // Change accuracy to the second + lastLogonDate = new DateTime((lastLogonDate.Ticks / 10000000L) * 10000000L); + + if (device.LastNetworkLogonDate.Value < lastLogonDate) + device.LastNetworkLogonDate = lastLogonDate; + } + } + } + } + + } +} diff --git a/Disco.Services/Interop/ActiveDirectory/ADUserAccount.cs b/Disco.Services/Interop/ActiveDirectory/ADUserAccount.cs new file mode 100644 index 00000000..fa923143 --- /dev/null +++ b/Disco.Services/Interop/ActiveDirectory/ADUserAccount.cs @@ -0,0 +1,173 @@ +using Disco.Models.Repository; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Principal; + +namespace Disco.Services.Interop.ActiveDirectory +{ + public class ADUserAccount : IADObject + { + internal const string LdapSamAccountNameFilterTemplate = "(&(objectCategory=Person)(sAMAccountName={0}))"; + internal const string LdapSearchFilterTemplate = "(&(objectCategory=Person)(objectClass=user)(|(sAMAccountName=*{0}*)(displayName=*{0}*)))"; + internal static readonly string[] LoadProperties = { "name", "distinguishedName", "sAMAccountName", "objectSid", "displayName", "sn", "givenName", "memberOf", "primaryGroupID", "mail", "telephoneNumber" }; + internal static readonly string[] QuickLoadProperties = { "name", "distinguishedName", "sAMAccountName", "objectSid", "displayName", "sn", "givenName", "mail", "telephoneNumber" }; + + + public ADDomain Domain { get; private set; } + + public string DistinguishedName { get; private set; } + public SecurityIdentifier SecurityIdentifier { get; private set; } + + public string Id { get { return string.Format(@"{0}\{1}", Domain.NetBiosName, SamAccountName); } } + public string SamAccountName { get; private set; } + + public string Name { get; private set; } + public string DisplayName { get; private set; } + + public string Surname { get; private set; } + public string GivenName { get; private set; } + public string Email { get; private set; } + public string Phone { get; private set; } + + public List Groups { get; private set; } + + public Dictionary LoadedProperties { get; private set; } + + private ADUserAccount(ADDomain Domain, string DistinguishedName, SecurityIdentifier SecurityIdentifier, string SamAccountName, + string Name, string DisplayName, string Surname, string GivenName, string Email, string Phone, + List Groups, Dictionary LoadedProperties) + { + this.Domain = Domain; + this.DistinguishedName = DistinguishedName; + this.SecurityIdentifier = SecurityIdentifier; + this.SamAccountName = SamAccountName; + this.Name = Name; + this.DisplayName = DisplayName; + this.Surname = Surname; + this.GivenName = GivenName; + this.Email = Email; + this.Phone = Phone; + this.Groups = Groups; + this.LoadedProperties = LoadedProperties; + } + + public static ADUserAccount FromSearchResult(ADSearchResult SearchResult, bool Quick, string[] AdditionalProperties) + { + if (SearchResult == null) + throw new ArgumentNullException("SearchResult"); + + string name = SearchResult.Value("name"); + string sAMAccountName = SearchResult.Value("sAMAccountName"); + string distinguishedName = SearchResult.Value("distinguishedName"); + var objectSid = new SecurityIdentifier(SearchResult.Value("objectSid"), 0); + + var displayName = SearchResult.Value("displayName") ?? sAMAccountName; + var surname = SearchResult.Value("sn"); + string givenName = SearchResult.Value("givenName"); + string email = SearchResult.Value("mail"); + string phone = SearchResult.Value("telephoneNumber"); + + List groups = null; + // Don't load Groups when doing a quick search + if (!Quick) + { + int primaryGroupID = (int)SearchResult.Value("primaryGroupID"); + var primaryGroupSid = ADHelpers.BuildPrimaryGroupSid(objectSid, primaryGroupID); + var memberGroups = SearchResult.Values("memberOf"); + + var primaryGroup = ActiveDirectory.GroupCache.GetGroup(primaryGroupSid); + + var groupDistinguishedNames = + new string[] { primaryGroup.DistinguishedName } + .Concat(memberGroups); + + groups = ActiveDirectory.GroupCache.GetRecursiveGroups(groupDistinguishedNames).ToList(); + } + + // Additional Properties + Dictionary additionalProperties; + if (AdditionalProperties != null) + additionalProperties = AdditionalProperties + .Select(p => Tuple.Create(p, SearchResult.Values(p).ToArray())) + .ToDictionary(t => t.Item1, t => t.Item2); + else + { + additionalProperties = new Dictionary(); + } + + return new ADUserAccount( + SearchResult.Domain, + distinguishedName, + objectSid, + sAMAccountName, + name, + displayName, + surname, + givenName, + email, + phone, + groups, + additionalProperties); + } + + public object GetPropertyValue(string PropertyName, int Index = 0) + { + switch (PropertyName.ToLower()) + { + case "name": + return this.Name; + case "samaccountname": + return this.SamAccountName; + case "distinguishedname": + return this.DistinguishedName; + case "objectsid": + return this.SecurityIdentifier.ToString(); + case "sn": + return this.Surname; + case "givenname": + return this.GivenName; + case "mail": + return this.Email; + case "telephonenumber": + return this.Phone; + default: + object[] adProperty; + if (this.LoadedProperties.TryGetValue(PropertyName, out adProperty) && Index <= adProperty.Length) + return adProperty[Index]; + else + return null; + } + } + + public User ToRepositoryUser() + { + return new User + { + UserId = this.Id, + DisplayName = this.DisplayName, + Surname = this.Surname, + GivenName = this.GivenName, + EmailAddress = this.Email, + PhoneNumber = this.Phone, + }; + } + + public override string ToString() + { + return this.Id; + } + + public override bool Equals(object obj) + { + if (obj == null || !(obj is ADUserAccount)) + return false; + else + return this.DistinguishedName == ((ADUserAccount)obj).DistinguishedName; + } + public override int GetHashCode() + { + return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this.DistinguishedName); + } + } +} diff --git a/Disco.Services/Interop/ActiveDirectory/ActiveDirectory.cs b/Disco.Services/Interop/ActiveDirectory/ActiveDirectory.cs index 189a5315..40302a34 100644 --- a/Disco.Services/Interop/ActiveDirectory/ActiveDirectory.cs +++ b/Disco.Services/Interop/ActiveDirectory/ActiveDirectory.cs @@ -1,595 +1,226 @@ using Disco.Data.Repository; -using Disco.Models.Interop.ActiveDirectory; -using Disco.Services.Interop.ActiveDirectory.Internal; using System; using System.Collections.Generic; -using System.DirectoryServices; -using System.DirectoryServices.ActiveDirectory; +using System.Diagnostics; using System.Linq; +using System.Security.Principal; +using System.Text; using System.Threading.Tasks; namespace Disco.Services.Interop.ActiveDirectory { public static class ActiveDirectory { - private const int SingleSearchResult = 1; + public const int SingleSearchResult = 1; + public const int DefaultSearchResultLimit = 30; public const int MaxForestServerSearch = 30; + public const int DomainControllerUnavailableMinutes = 10; + + private static ActiveDirectoryContext context; + private static ActiveDirectoryGroupCache groupCache; + private static object contextInitializingLock = new object(); public static void Initialize(DiscoDataContext Database) { - ADInterop.Initialize(Database); - } - public static void UpdateSearchContainers(DiscoDataContext Database, List Containers) - { - ADInterop.UpdateSearchContainers(Database, Containers); - } - public static bool UpdateSearchEntireForest(DiscoDataContext Database, bool SearchEntireForest) - { - return ADInterop.UpdateSearchEntireForest(Database, SearchEntireForest); - } - - public static ActiveDirectoryDomain PrimaryDomain - { - get + lock (contextInitializingLock) { - return ADInterop.PrimaryDomain; - } - } - public static IEnumerable Domains - { - get - { - return ADInterop.Domains.ToList(); + context = new ActiveDirectoryContext(Database); + groupCache = new ActiveDirectoryGroupCache(); } } - public static ActiveDirectorySite Site + public static ActiveDirectoryContext Context { get { - return ADInterop.Site; - } - } - - public static ActiveDirectoryDomain GetDomainByDistinguishedName(string DistinguishedName) - { - return ADInterop.GetDomainByDistinguishedName(DistinguishedName); - } - - public static ActiveDirectoryDomain GetDomainByNetBiosName(string NetBiosName) - { - return ADInterop.GetDomainByNetBiosName(NetBiosName); - } - - public static ActiveDirectoryDomain GetDomainByDnsName(string DnsName) - { - return ADInterop.GetDomainByDnsName(DnsName); - } - - public static ActiveDirectoryDomain GetDomainFromId(string Id) - { - return ADInterop.GetDomainFromId(Id); - } - - public static List LoadForestServers() - { - return ADInterop.LoadForestServers(); - } - - public static Task> LoadForestServersAsync() - { - return ADInterop.LoadForestServersAsync(); - } - - public static bool SearchEntireForest - { - get - { - return ADInterop.SearchEntireForest; - } - } - - #region Machine Account - private static readonly string[] MachineLoadProperties = { "name", "distinguishedName", "sAMAccountName", "objectSid", "dNSHostName", "netbootGUID", "isCriticalSystemObject" }; - - public static ActiveDirectoryMachineAccount RetrieveMachineAccount(DomainController DomainController, string Id, params string[] AdditionalProperties) - { - return RetrieveMachineAccount(DomainController, Id, (System.Guid?)null, (System.Guid?)null, AdditionalProperties); - } - public static ActiveDirectoryMachineAccount RetrieveMachineAccount(string Id, params string[] AdditionalProperties) - { - return RetrieveMachineAccount(Id, (System.Guid?)null, (System.Guid?)null, AdditionalProperties); - } - public static ActiveDirectoryMachineAccount RetrieveMachineAccount(DomainController DomainController, string Id, System.Guid? UUIDNetbootGUID, params string[] AdditionalProperties) - { - return RetrieveMachineAccount(DomainController, Id, UUIDNetbootGUID, (System.Guid?)null, AdditionalProperties); - } - public static ActiveDirectoryMachineAccount RetrieveMachineAccount(string Id, System.Guid? UUIDNetbootGUID, params string[] AdditionalProperties) - { - return RetrieveMachineAccount(Id, UUIDNetbootGUID, (System.Guid?)null, AdditionalProperties); - } - public static ActiveDirectoryMachineAccount RetrieveMachineAccount(string Id, System.Guid? UUIDNetbootGUID, System.Guid? MacAddressNetbootGUID, params string[] AdditionalProperties) - { - return RetrieveMachineAccount(null, Id, UUIDNetbootGUID, MacAddressNetbootGUID, AdditionalProperties); - } - public static ActiveDirectoryMachineAccount RetrieveMachineAccount(DomainController DomainController, string Id, System.Guid? UUIDNetbootGUID, System.Guid? MacAddressNetbootGUID, params string[] AdditionalProperties) - { - if (string.IsNullOrWhiteSpace(Id)) - throw new ArgumentNullException("Id"); - - // Add $ identifier for machine accounts - if (!Id.EndsWith("$")) - Id += "$"; - - const string ldapFilterTemplate = "(&(objectCategory=computer)(sAMAccountName={0}))"; - const string ldapNetbootGuidFilterTemplate = "(&(objectCategory=computer)(netbootGUID={0}))"; - - string[] loadProperites = (AdditionalProperties != null && AdditionalProperties.Length > 0) - ? MachineLoadProperties.Concat(AdditionalProperties).ToArray() - : MachineLoadProperties; - - ActiveDirectorySearchResult adResult; - - var splitId = UserExtensions.SplitUserId(Id); - var ldapSamAccountFilter = string.Format(ldapFilterTemplate, splitId.Item2); - var domain = ADInterop.GetDomainFromId(Id); - - adResult = ADInterop.SearchAll(domain, DomainController, ldapSamAccountFilter, SingleSearchResult, loadProperites).FirstOrDefault(); - if (adResult == null && (UUIDNetbootGUID.HasValue || MacAddressNetbootGUID.HasValue)) - { - - if (UUIDNetbootGUID.HasValue) + if (context == null) { - var ldapFilter = string.Format(ldapNetbootGuidFilterTemplate, ADInterop.FormatGuidForLdapQuery(UUIDNetbootGUID.Value)); - adResult = ADInterop.SearchAll(domain, DomainController, ldapFilter, SingleSearchResult, loadProperites).FirstOrDefault(); + lock (contextInitializingLock) + { + if (context == null) + throw new InvalidOperationException("Active Directory interoperability hasn't been initialized"); + } } - if (adResult == null && MacAddressNetbootGUID.HasValue) - { - var ldapFilter = string.Format(ldapNetbootGuidFilterTemplate, ADInterop.FormatGuidForLdapQuery(MacAddressNetbootGUID.Value)); - adResult = ADInterop.SearchAll(domain, DomainController, ldapFilter, SingleSearchResult, loadProperites).FirstOrDefault(); - } - } - if (adResult != null) - return adResult.AsMachineAccount(AdditionalProperties); + return context; + } + } + public static ActiveDirectoryGroupCache GroupCache + { + get + { + if (groupCache == null) + { + lock (contextInitializingLock) + { + if (groupCache == null) + throw new InvalidOperationException("Active Directory interoperability hasn't been initialized"); + } + } + + return groupCache; + } + } + + #region User Accounts + + public static ADUserAccount RetrieveADUserAccount(string Id, params string[] AdditionalProperties) + { + var domain = Context.GetDomainFromId(Id); + return domain.GetAvailableDomainController().RetrieveADUserAccount(Id, AdditionalProperties); + } + + public static IEnumerable SearchADUserAccounts(string Term, bool Quick, int? ResultLimit = ActiveDirectory.DefaultSearchResultLimit, params string[] AdditionalProperties) + { + if (string.IsNullOrWhiteSpace(Term)) + throw new ArgumentNullException("Term"); + + ADDomain searchDomain; + var term = RelevantSearchTerm(Term, out searchDomain); + + if (string.IsNullOrWhiteSpace(term)) + return Enumerable.Empty(); + + var ldapFilter = string.Format(ADUserAccount.LdapSearchFilterTemplate, ADHelpers.EscapeLdapQuery(term)); + + IEnumerable searchResults; + if (searchDomain != null) + searchResults = searchDomain.SearchScope(ldapFilter, ADGroup.LoadProperties, ResultLimit); else - return null; // Not Found - } + searchResults = Context.SearchScope(ldapFilter, ADGroup.LoadProperties, ResultLimit); - private static ActiveDirectoryMachineAccount AsMachineAccount(this ActiveDirectorySearchResult item, string[] AdditionalProperties) - { - string name = item.Result.Properties["name"][0].ToString(); - string sAMAccountName = item.Result.Properties["sAMAccountName"][0].ToString(); - string distinguishedName = item.Result.Properties["distinguishedName"][0].ToString(); - string objectSid = ADInterop.ConvertBytesToSDDLString((byte[])item.Result.Properties["objectSid"][0]); - - var dNSNameProperty = item.Result.Properties["dNSHostName"]; - string dNSName = null; - if (dNSNameProperty.Count > 0) - dNSName = dNSNameProperty[0].ToString(); - else - dNSName = string.Format("{0}.{1}", sAMAccountName.TrimEnd('$'), item.Domain.DnsName); - - bool isCriticalSystemObject = (bool)item.Result.Properties["isCriticalSystemObject"][0]; - - System.Guid netbootGUIDResult = default(System.Guid); - ResultPropertyValueCollection netbootGUIDProp = item.Result.Properties["netbootGUID"]; - if (netbootGUIDProp.Count > 0) - { - netbootGUIDResult = new System.Guid((byte[])netbootGUIDProp[0]); - } - - // Additional Properties - Dictionary additionalProperties = new Dictionary(); - if (AdditionalProperties != null) - foreach (string propertyName in AdditionalProperties) - { - var property = item.Result.Properties[propertyName]; - var propertyValues = new List(); - for (int index = 0; index < property.Count; index++) - propertyValues.Add(property[index]); - additionalProperties.Add(propertyName, propertyValues.ToArray()); - } - - return new ActiveDirectoryMachineAccount - { - Name = name, - DistinguishedName = distinguishedName, - SamAccountName = sAMAccountName, - SecurityIdentifier = objectSid, - NetbootGUID = netbootGUIDResult, - Path = item.Result.Path, - Domain = item.Domain.NetBiosName, - DnsName = dNSName, - IsCriticalSystemObject = isCriticalSystemObject, - LoadedProperties = additionalProperties - }; - } - - public static string OfflineDomainJoinProvision(ActiveDirectoryDomain Domain, DomainController DomainController, string ComputerName, string OrganisationalUnit, ref ActiveDirectoryMachineAccount MachineAccount, out string DiagnosticInformation) - { - const string ldapFilterTemplate = "(&(objectCategory=computer)(sAMAccountName={0}))"; - - if (MachineAccount != null && MachineAccount.IsCriticalSystemObject) - throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", MachineAccount.DistinguishedName)); - - if (MachineAccount != null) - MachineAccount.DeleteAccount(DomainController); - - var computerId = UserExtensions.SplitUserId(ComputerName); - - var offlineJoinResult = ADInterop.OfflineDomainJoinProvision(Domain, DomainController, computerId.Item2, OrganisationalUnit, out DiagnosticInformation); - - // Reload newly created Account - string[] loadAdditionalProperties = (MachineAccount != null && MachineAccount.LoadedProperties != null && MachineAccount.LoadedProperties.Count > 0) - ? MachineAccount.LoadedProperties.Keys.ToArray() - : null; - - MachineAccount = null; - - string[] loadProperites = (loadAdditionalProperties != null) - ? MachineLoadProperties.Concat(loadAdditionalProperties).ToArray() - : MachineLoadProperties; - - var ldapSamAccountName = computerId.Item2.EndsWith("$") ? computerId.Item2 : computerId.Item2 + "$"; - var ldapFilter = string.Format(ldapFilterTemplate, ldapSamAccountName); - var ldapResult = ADInterop.SearchAll(Domain, DomainController, ldapFilter, 1, loadProperites).FirstOrDefault(); - - if (ldapResult != null) - MachineAccount = ldapResult.AsMachineAccount(loadAdditionalProperties); - - return offlineJoinResult; + return searchResults.Select(result => result.AsADUserAccount(Quick, AdditionalProperties)); } #endregion - #region User Account - private static readonly string[] UserLoadProperties = { "name", "distinguishedName", "sAMAccountName", "objectSid", "displayName", "sn", "givenName", "memberOf", "primaryGroupID", "mail", "telephoneNumber" }; - private static readonly string[] UserQuickLoadProperties = { "name", "distinguishedName", "sAMAccountName", "objectSid", "displayName", "sn", "givenName", "mail", "telephoneNumber" }; - - public static ActiveDirectoryUserAccount RetrieveUserAccount(string Id, params string[] AdditionalProperties) + #region Machine Accounts + public static ADMachineAccount RetrieveADMachineAccount(string Id, params string[] AdditionalProperties) { - const string ldapFilter = "(&(objectCategory=Person)(sAMAccountName={0}))"; - - string[] loadProperites = (AdditionalProperties != null && AdditionalProperties.Length > 0) - ? UserLoadProperties.Concat(AdditionalProperties).ToArray() - : UserLoadProperties; - - return SearchBySamAccountName(Id, ldapFilter, loadProperites).Select(result => result.AsUserAccount(false, AdditionalProperties)).FirstOrDefault(); + var domain = Context.GetDomainFromId(Id); + return domain.GetAvailableDomainController().RetrieveADMachineAccount(Id, AdditionalProperties); } - public static IEnumerable SearchUserAccounts(string Term, params string[] AdditionalProperties) + public static ADMachineAccount RetrieveADMachineAccount(string Id, System.Guid? NetbootGUID, params string[] AdditionalProperties) { - return SearchUserAccounts(Term, false, AdditionalProperties); + var domain = Context.GetDomainFromId(Id); + return domain.GetAvailableDomainController().RetrieveADMachineAccount(Id, NetbootGUID, AdditionalProperties); } - public static IEnumerable SearchUserAccounts(string Term, bool Quick, params string[] AdditionalProperties) + public static ADMachineAccount RetrieveADMachineAccount(string Id, System.Guid? UUIDNetbootGUID, System.Guid? MacAddressNetbootGUID, params string[] AdditionalProperties) { - const int resultLimit = 30; // Default Search Limit - - if (string.IsNullOrWhiteSpace(Term)) - throw new ArgumentNullException("Term"); - - // Apply Domain Restriction - ActiveDirectoryDomain searchDomain = null; - Term = ApplySearchTermDomainRestriction(Term, out searchDomain); - - if (string.IsNullOrWhiteSpace(Term)) - return Enumerable.Empty(); - - string ldapFilter = string.Format("(&(objectCategory=Person)(objectClass=user)(|(sAMAccountName=*{0}*)(displayName=*{0}*)))", ADInterop.EscapeLdapQuery(Term)); - - string[] loadProperites = Quick - ? UserQuickLoadProperties - : UserLoadProperties; - - if (AdditionalProperties != null && AdditionalProperties.Length > 0) - loadProperites.Concat(AdditionalProperties).ToArray(); - - IEnumerable searchResults; - if (searchDomain == null) - searchResults = ADInterop.SearchScope(ldapFilter, resultLimit, loadProperites); - else - searchResults = ADInterop.SearchScope(searchDomain, ldapFilter, resultLimit, loadProperites); - - return searchResults.Select(result => result.AsUserAccount(Quick, AdditionalProperties)); - } - - private static ActiveDirectoryUserAccount AsUserAccount(this ActiveDirectorySearchResult item, bool Quick, string[] AdditionalProperties) - { - string name = item.Result.Properties["name"][0].ToString(); - string username = item.Result.Properties["sAMAccountName"][0].ToString(); - string distinguishedName = item.Result.Properties["distinguishedName"][0].ToString(); - byte[] objectSid = (byte[])item.Result.Properties["objectSid"][0]; - string objectSidSDDL = ADInterop.ConvertBytesToSDDLString(objectSid); - List groups = null; - - ResultPropertyValueCollection displayNameProp = item.Result.Properties["displayName"]; - string displayName = username; - if (displayNameProp.Count > 0) - displayName = displayNameProp[0].ToString(); - string surname = null; - ResultPropertyValueCollection surnameProp = item.Result.Properties["sn"]; - if (surnameProp.Count > 0) - surname = surnameProp[0].ToString(); - string givenName = null; - ResultPropertyValueCollection givenNameProp = item.Result.Properties["givenName"]; - if (givenNameProp.Count > 0) - givenName = givenNameProp[0].ToString(); - string email = null; - ResultPropertyValueCollection emailProp = item.Result.Properties["mail"]; - if (emailProp.Count > 0) - email = emailProp[0].ToString(); - string phone = null; - ResultPropertyValueCollection phoneProp = item.Result.Properties["telephoneNumber"]; - if (phoneProp.Count > 0) - phone = phoneProp[0].ToString(); - - // Don't load Groups when doing a quick search - if (!Quick) - { - int primaryGroupID = (int)item.Result.Properties["primaryGroupID"][0]; - string primaryGroupSid = ADInterop.ConvertBytesToSDDLString(ADInterop.BuildPrimaryGroupSid(objectSid, primaryGroupID)); - var groupDistinguishedNames = item.Result.Properties["memberOf"].Cast().ToList(); - groupDistinguishedNames.Add(ADGroupCache.GetGroupsDistinguishedNameForSecurityIdentifier(primaryGroupSid)); - groups = ADGroupCache.GetGroups(groupDistinguishedNames).ToList(); - } - - // Additional Properties - Dictionary additionalProperties = new Dictionary(); - if (AdditionalProperties != null && AdditionalProperties.Length > 0) - foreach (string propertyName in AdditionalProperties) - { - var property = item.Result.Properties[propertyName]; - var propertyValues = new List(); - for (int index = 0; index < property.Count; index++) - propertyValues.Add(property[index]); - additionalProperties.Add(propertyName, propertyValues.ToArray()); - } - - return new ActiveDirectoryUserAccount - { - Domain = item.Domain.NetBiosName, - Name = name, - Surname = surname, - GivenName = givenName, - Email = email, - Phone = phone, - DistinguishedName = distinguishedName, - SamAccountName = username, - DisplayName = displayName, - SecurityIdentifier = objectSidSDDL, - Groups = groups, - Path = item.Result.Path, - LoadedProperties = additionalProperties - }; + var domain = Context.GetDomainFromId(Id); + return domain.GetAvailableDomainController().RetrieveADMachineAccount(Id, UUIDNetbootGUID, MacAddressNetbootGUID, AdditionalProperties); } #endregion #region Groups - private static readonly string[] GroupLoadProperties = { "name", "distinguishedName", "cn", "sAMAccountName", "objectSid", "memberOf" }; - public static ActiveDirectoryGroup RetrieveGroup(string Id) + public static ADGroup RetrieveADGroup(string Id) { - const string ldapFilter = "(&(objectCategory=Group)(objectSid={0}))"; - - return SearchBySamAccountName(Id, ldapFilter, GroupLoadProperties).Select(result => result.AsGroup()).FirstOrDefault(); + var domain = Context.GetDomainFromId(Id); + return domain.GetAvailableDomainController().RetrieveADGroup(Id); } - public static ActiveDirectoryGroup RetrieveGroupWithDistinguishedName(string DistinguishedName) + public static ADGroup RetrieveADGroupByDistinguishedName(string DistinguishedName) { - ActiveDirectoryDomain domain; - - using (var groupEntry = ADInterop.RetrieveDirectoryEntry(DistinguishedName, out domain)) - { - if (groupEntry == null) - return null; - - return groupEntry.AsGroup(domain); - } + var domain = Context.GetDomainFromDistinguishedName(DistinguishedName); + return domain.GetAvailableDomainController().RetrieveADGroupByDistinguishedName(DistinguishedName); } - public static ActiveDirectoryGroup RetrieveGroupWithSecurityIdentifier(string SecurityIdentifier) + public static ADGroup RetrieveADGroupWithSecurityIdentifier(SecurityIdentifier SecurityIdentifier) { - const int resultLimit = 1; - - if (string.IsNullOrWhiteSpace(SecurityIdentifier)) - throw new ArgumentNullException("SecurityIdentifier"); - - var sidBytes = ADInterop.ConvertSDDLStringToBytes(SecurityIdentifier); - var sidBinaryString = ADInterop.ConvertBytesToBinarySidString(sidBytes); - - string ldapFilter = string.Format("(&(objectCategory=Group)(objectSid={0}))", sidBinaryString); - - return ADInterop.SearchAll(ldapFilter, resultLimit, null).Select(result => result.AsGroup()).FirstOrDefault(); + var domain = Context.GetDomainFromSecurityIdentifier(SecurityIdentifier); + return domain.GetAvailableDomainController().RetrieveADGroupWithSecurityIdentifier(SecurityIdentifier); } - public static IEnumerable SearchGroups(string Term) - { - const int resultLimit = 30; // Default Search Limit + public static IEnumerable SearchADGroups(string Term, int? ResultLimit = ActiveDirectory.DefaultSearchResultLimit) + { if (string.IsNullOrWhiteSpace(Term)) throw new ArgumentNullException("Term"); - // Apply Domain Restriction - ActiveDirectoryDomain searchDomain = null; - Term = ApplySearchTermDomainRestriction(Term, out searchDomain); + ADDomain searchDomain; + var term = RelevantSearchTerm(Term, out searchDomain); - if (string.IsNullOrWhiteSpace(Term)) - return Enumerable.Empty(); + if (string.IsNullOrWhiteSpace(term)) + return Enumerable.Empty(); - string ldapFilter = string.Format("(&(objectCategory=Group)(|(sAMAccountName=*{0}*)(name=*{0}*)(cn=*{0}*)))", ADInterop.EscapeLdapQuery(Term)); + var ldapFilter= string.Format(ADGroup.LdapSearchFilterTemplate, ADHelpers.EscapeLdapQuery(term)); - IEnumerable searchResults; - if (searchDomain == null) - searchResults = ADInterop.SearchScope(ldapFilter, resultLimit, GroupLoadProperties); + IEnumerable searchResults; + if (searchDomain != null) + searchResults = searchDomain.SearchScope(ldapFilter, ADGroup.LoadProperties, ResultLimit); else - searchResults = ADInterop.SearchScope(searchDomain, ldapFilter, resultLimit, GroupLoadProperties); + searchResults = Context.SearchScope(ldapFilter, ADGroup.LoadProperties, ResultLimit); - return searchResults.Select(result => result.AsGroup()); + return searchResults.Select(result => result.AsADGroup()); } - private static ActiveDirectoryGroup AsGroup(this ActiveDirectorySearchResult item) - { - var name = (string)item.Result.Properties["name"][0]; - var distinguishedName = (string)item.Result.Properties["distinguishedName"][0]; - var cn = (string)item.Result.Properties["cn"][0]; - var sAMAccountName = (string)item.Result.Properties["sAMAccountName"][0]; - var objectSid = ADInterop.ConvertBytesToSDDLString((byte[])item.Result.Properties["objectSid"][0]); - var memberOf = item.Result.Properties["memberOf"].Cast().ToList(); + #endregion - return new ActiveDirectoryGroup() - { - Domain = item.Domain.NetBiosName, - Name = name, - DistinguishedName = distinguishedName, - CommonName = cn, - SamAccountName = sAMAccountName, - SecurityIdentifier = objectSid, - MemberOf = memberOf - }; - } - private static ActiveDirectoryGroup AsGroup(this DirectoryEntry item, ActiveDirectoryDomain Domain) + #region Organisational Units + public static IEnumerable>> RetrieveADOrganisationalUnitStructure() { - var name = (string)item.Properties["name"][0]; - var distinguishedName = (string)item.Properties["distinguishedName"][0]; - var cn = (string)item.Properties["cn"][0]; - var sAMAccountName = (string)item.Properties["sAMAccountName"][0]; - var objectSid = ADInterop.ConvertBytesToSDDLString((byte[])item.Properties["objectSid"][0]); - var memberOf = item.Properties["memberOf"].Cast().ToList(); - - return new ActiveDirectoryGroup() - { - Domain = Domain.NetBiosName, - Name = name, - DistinguishedName = distinguishedName, - CommonName = cn, - SamAccountName = sAMAccountName, - SecurityIdentifier = objectSid, - MemberOf = memberOf - }; + return Context.Domains + .Select(d => d.GetAvailableDomainController()) + .Select(dc => Tuple.Create(dc.Domain, dc.RetrieveADOrganisationalUnitStructure())); } #endregion - #region Object - private static readonly string[] ObjectLoadProperties = { "objectCategory" }; - private static readonly string[] ObjectLoadPropertiesAll = ObjectLoadProperties.Concat(UserLoadProperties).Concat(MachineLoadProperties).Concat(GroupLoadProperties).Distinct().ToArray(); - - public static IActiveDirectoryObject RetrieveObject(string Id, bool Quick) + #region Objects + public static IADObject RetrieveADObject(string Id, bool Quick) { - const string ldapFilter = "(&(|(objectCategory=Person)(objectCategory=Computer)(objectCategory=Group))(sAMAccountName={0}))"; - - return SearchBySamAccountName(Id, ldapFilter, ObjectLoadPropertiesAll) - .Select(result => - { - var objectCategory = (string)result.Result.Properties["objectCategory"][0]; - objectCategory = objectCategory.Substring(0, objectCategory.IndexOf(',')).ToLower(); - switch (objectCategory) - { - case "cn=person": - return result.AsUserAccount(Quick, null); - case "cn=computer": - return result.AsMachineAccount(null); - case "cn=group": - return result.AsGroup(); - default: - throw new InvalidOperationException("Unexpected objectCategory"); - } - }).FirstOrDefault(); + var domain = Context.GetDomainFromId(Id); + return domain.GetAvailableDomainController().RetrieveADObject(Id, Quick); } #endregion - #region Organisation Units + #region Actions - public static List RetrieveOrganisationalUnitStructure(ActiveDirectoryDomain Domain) + public static string OfflineDomainJoinProvision(string ComputerSamAccountName, string OrganisationalUnit, ref ADMachineAccount MachineAccount, out string DiagnosticInformation) { - using (DirectoryEntry domainRoot = ADInterop.RetrieveDirectoryEntry(Domain.DistinguishedName, out Domain)) - { - return ActiveDirectory.RetrieveOrganisationalUnitStructureInternal(Domain, domainRoot); - } - } - private static List RetrieveOrganisationalUnitStructureInternal(ActiveDirectoryDomain Domain, DirectoryEntry Container) - { - Dictionary> resultTree = new Dictionary>(); + var domain = Context.GetDomainFromDistinguishedName(OrganisationalUnit); + var writableDomainController = domain.GetAvailableDomainController(RequireWritable: true); - using (DirectorySearcher adSearcher = new DirectorySearcher(Container, "(objectCategory=organizationalUnit)", new string[] - { - "name", - "distinguishedName" - }, SearchScope.Subtree)) - { - adSearcher.PageSize = 500; - - using (SearchResultCollection adResults = adSearcher.FindAll()) - { - resultTree = adResults.Cast().Select(adResult => - { - string i = adResult.Properties["name"][0].ToString(); - string dn = adResult.Properties["distinguishedName"][0].ToString(); - return new ActiveDirectoryOrganisationalUnit - { - Domain = Domain.NetBiosName, - Name = i, - DistinguishedName = dn, - }; - }).GroupBy(ou => ou.DistinguishedName.Substring(ou.DistinguishedName.IndexOf(',') + 1)).ToDictionary(g => g.Key, g => g.ToList()); - } - } - - // Build Tree - var results = resultTree[Domain.DistinguishedName]; - foreach (var child in results) - RetrieveOrganisationalUnitStructureChildrenInternal(child, resultTree); - - return results; - } - private static void RetrieveOrganisationalUnitStructureChildrenInternal(ActiveDirectoryOrganisationalUnit OrganisationalUnit, Dictionary> ResultTree) - { - List children; - - if (ResultTree.TryGetValue(OrganisationalUnit.DistinguishedName, out children)) - { - foreach (var child in children) - RetrieveOrganisationalUnitStructureChildrenInternal(child, ResultTree); - - OrganisationalUnit.Children = children; - } + return writableDomainController.OfflineDomainJoinProvision(ComputerSamAccountName, OrganisationalUnit, ref MachineAccount, out DiagnosticInformation); } #endregion #region Helpers - private static IEnumerable SearchBySamAccountName(string Id, string LdapFilterTemplate, string[] LoadProperties) + private static string RelevantSearchTerm(string Term, out ADDomain Domain) { - var splitId = UserExtensions.SplitUserId(Id); - var ldapFilter = string.Format(LdapFilterTemplate, splitId.Item2); - var domains = ADInterop.GetDomainFromId(Id); + Domain = null; - return ADInterop.SearchAll(domains, ldapFilter, SingleSearchResult, LoadProperties); - } - - private static string ApplySearchTermDomainRestriction(string Term, out ActiveDirectoryDomain Domain) - { if (string.IsNullOrWhiteSpace(Term)) - throw new ArgumentNullException("Term"); + return null; - var domainIndex = Term.IndexOf('\\'); - if (domainIndex >= 0) + var term = Term.Trim(); + + var domainSeperatorIndex = term.IndexOf('\\'); + + if (domainSeperatorIndex >= 0) { - var domain = Term.Substring(0, domainIndex); + // Domain Search Restriction - if (!ADInterop.TryGetDomainByNetBiosName(domain, out Domain)) - return null; // Domain not found - invalid search - - if (Term.Length > (domainIndex + 1)) - return Term.Substring(domainIndex + 1); + if (term.Length > domainSeperatorIndex + 1) + { + var netbiosName = term.Substring(0, domainSeperatorIndex); + if (Context.TryGetDomainByNetBiosName(netbiosName, out Domain)) + { + return term.Substring(domainSeperatorIndex + 1); + } + else + { + return null; // Unknown Domain + } + } else - return null; // Domain only, no Term - } - else - { - Domain = null; - return Term; + { + return null; // No term to search, only Domain + } } + + return term; } #endregion - } -} \ No newline at end of file +} diff --git a/Disco.Services/Interop/ActiveDirectory/ActiveDirectoryContext.cs b/Disco.Services/Interop/ActiveDirectory/ActiveDirectoryContext.cs new file mode 100644 index 00000000..399dbe6b --- /dev/null +++ b/Disco.Services/Interop/ActiveDirectory/ActiveDirectoryContext.cs @@ -0,0 +1,308 @@ +using Disco.Data.Repository; +using System; +using System.Collections.Generic; +using System.DirectoryServices; +using System.DirectoryServices.ActiveDirectory; +using System.Linq; +using System.Security.Principal; +using System.Text; +using System.Threading.Tasks; + +namespace Disco.Services.Interop.ActiveDirectory +{ + public class ActiveDirectoryContext + { + public ADSite Site { get; private set; } + public ADDomain PrimaryDomain { get; private set; } + public List Domains { get; private set; } + + public List ForestServers + { + get + { + return ADDiscoverForestServers.LoadForestServersBlocking(); + } + } + + private bool _SearchAllForestServers { get; set; } + public bool SearchAllForestServers + { + get + { + var fs = ADDiscoverForestServers.ForestServers; + if (fs != null && fs.Count > ActiveDirectory.MaxForestServerSearch) + return false; // Never + + return _SearchAllForestServers; + } + } + + #region Contructor/Initializing + + internal ActiveDirectoryContext(DiscoDataContext Database) + { + Initialize(Database); + } + + private void Initialize(DiscoDataContext Database) + { + // Search Entire Forest (default: true) + this._SearchAllForestServers = Database.DiscoConfiguration.ActiveDirectory.SearchAllForestServers ?? true; + + // Determine Site + var computerSite = ActiveDirectorySite.GetComputerSite(); + this.Site = new ADSite(this, computerSite); + + // Determine Domains + var computerDomain = Domain.GetComputerDomain(); + this.Domains = computerDomain.Forest.Domains + .Cast() + .Select(d => new ADDomain(this, d)) + .ToList(); + this.PrimaryDomain = this.Domains.Where(d => d.Name == computerDomain.Name).First(); + + // Determine Search Scope Containers + ReinitializeSearchContainers(Database.DiscoConfiguration.ActiveDirectory.SearchContainers); + + // Determine Domain Controllers + var siteDomainControllers = computerSite.Servers + .OfType() + .Where(dc => dc.IsReachable()) + .Select(dc => new ADDomainController(this, dc, GetDomainByName(dc.Domain.Name), IsSiteServer: true, IsWritable: false)); + + Site.UpdateDomainControllers(siteDomainControllers); + this.Domains.ForEach(domain => domain.UpdateDomainControllers(siteDomainControllers.Where(dc => dc.Domain == domain))); + } + + #endregion + + #region Domain Getters + + public bool TryGetDomainFromDistinguishedName(string DistinguishedName, out ADDomain Domain) + { + // Find closest match + Domain = this.Domains.Where(d => DistinguishedName.EndsWith(d.DistinguishedName, StringComparison.OrdinalIgnoreCase)) + .OrderByDescending(d => d.DistinguishedName.Length).FirstOrDefault(); + + return (Domain != null); + } + public ADDomain GetDomainFromDistinguishedName(string DistinguishedName) + { + ADDomain domain; + if (!TryGetDomainFromDistinguishedName(DistinguishedName, out domain)) + throw new ArgumentException(string.Format("The distinguished name is from an unknown domain: [{0}]", DistinguishedName), "DistinguishedName"); + return domain; + } + + public bool TryGetDomainByNetBiosName(string NetBiosName, out ADDomain Domain) + { + Domain = this.Domains.FirstOrDefault(d => d.NetBiosName.Equals(NetBiosName, StringComparison.OrdinalIgnoreCase)); + return (Domain != null); + } + public ADDomain GetDomainByNetBiosName(string NetBiosName) + { + ADDomain domain; + if (!TryGetDomainByNetBiosName(NetBiosName, out domain)) + throw new ArgumentException(string.Format("The domain for specified NetBios name is unknown [{0}]", NetBiosName), "NetBiosName"); + return domain; + } + + public bool TryGetDomainByName(string Name, out ADDomain Domain) + { + Domain = this.Domains.FirstOrDefault(d => d.Name.Equals(Name, StringComparison.OrdinalIgnoreCase)); + return (Domain != null); + } + public ADDomain GetDomainByName(string Name) + { + ADDomain domain; + if (!TryGetDomainByName(Name, out domain)) + throw new ArgumentException(string.Format("The domain for specified DNS name is unknown [{0}]", Name), "Name"); + return domain; + } + + public bool TryGetDomainFromSecurityIdentifier(SecurityIdentifier SecurityIdentifier, out ADDomain Domain) + { + Domain = this.Domains.FirstOrDefault(d => d.SecurityIdentifier.IsEqualDomainSid(SecurityIdentifier)); + return (Domain != null); + } + public ADDomain GetDomainFromSecurityIdentifier(SecurityIdentifier SecurityIdentifier) + { + ADDomain domain; + if (!TryGetDomainFromSecurityIdentifier(SecurityIdentifier, out domain)) + throw new ArgumentException(string.Format("The domain for specified Security Identifier is unknown [{0}]", SecurityIdentifier.ToString()), "SecurityIdentifier"); + return domain; + } + + public bool TryGetDomainFromId(string Id, out ADDomain Domain) + { + if (string.IsNullOrWhiteSpace(Id)) + throw new ArgumentNullException("Id"); + + var idSplit = UserExtensions.SplitUserId(Id); + + if (string.IsNullOrWhiteSpace(idSplit.Item1)) + throw new ArgumentException(string.Format("The Id must include the Domain [{0}]", Id), "Id"); + + return TryGetDomainByNetBiosName(idSplit.Item1, out Domain); + } + public ADDomain GetDomainFromId(string Id) + { + if (string.IsNullOrWhiteSpace(Id)) + throw new ArgumentNullException("Id"); + + var idSplit = UserExtensions.SplitUserId(Id); + + if (string.IsNullOrWhiteSpace(idSplit.Item1)) + throw new ArgumentException(string.Format("The Id must include the Domain [{0}]", Id), "Id"); + + return GetDomainByNetBiosName(idSplit.Item1); + } + + #endregion + + public ADDirectoryEntry RetrieveDirectoryEntry(string DistinguishedName, string[] LoadProperties = null) + { + if (string.IsNullOrWhiteSpace(DistinguishedName)) + throw new ArgumentNullException("DistinguishedName"); + + var d = GetDomainFromDistinguishedName(DistinguishedName); + var dc = d.GetAvailableDomainController(); + + return dc.RetrieveDirectoryEntry(DistinguishedName, LoadProperties); + } + + #region Searching + + public IEnumerable SearchEntireForest(string LdapFilter, string[] LoadProperties, int? ResultLimit = null) + { + var queries = this.Domains.Select(d => Tuple.Create(d, d.DistinguishedName)); + + return SearchInternal(queries, LdapFilter, LoadProperties, ResultLimit); + } + + public IEnumerable SearchScope(string LdapFilter, string[] LoadProperties, int? ResultLimit = null) + { + var queries = this.Domains.SelectMany( + d => d.SearchContainers ?? new List() { d.DistinguishedName }, + (d, scope) => Tuple.Create(d, scope)); + + return SearchInternal(queries, LdapFilter, LoadProperties, ResultLimit); + } + + internal IEnumerable SearchInternal(IEnumerable> Queries, string LdapFilter, string[] LoadProperties, int? ResultLimit) + { + var queries = Queries.ToList(); + + switch (queries.Count) + { + case 0: // Nothing Queried + return Enumerable.Empty(); + + case 1: // Single-search + var querySingle = queries[0]; + return querySingle.Item1.SearchInternal(querySingle.Item2, LdapFilter, LoadProperties, ResultLimit); + + default: // Multi-search - Parallelize + + var queryTasks = queries.Select(query => + Task>.Factory.StartNew(() => + query.Item1.SearchInternal(query.Item2, LdapFilter, LoadProperties, ResultLimit))).ToArray(); + + // Block + Task.WaitAll(queryTasks); + + var results = queryTasks.SelectMany(t => t.Result); + if (ResultLimit.HasValue) + results = results.Take(ResultLimit.Value); + return results; + } + } + + #endregion + + #region Configuration + + public bool UpdateSearchAllForestServers(DiscoDataContext Database, bool SearchAllForestServers) + { + if (SearchAllForestServers == false) + { + Database.DiscoConfiguration.ActiveDirectory.SearchAllForestServers = false; + this._SearchAllForestServers = false; + return true; + } + else + { + var forestServers = ADDiscoverForestServers.LoadForestServersBlocking(); + if (forestServers.Count <= ActiveDirectory.MaxForestServerSearch) + { + Database.DiscoConfiguration.ActiveDirectory.SearchAllForestServers = true; + this._SearchAllForestServers = true; + return true; + } + else + { + Database.DiscoConfiguration.ActiveDirectory.SearchAllForestServers = false; + this._SearchAllForestServers = false; + return false; + } + } + } + + public void UpdateSearchContainers(DiscoDataContext Database, IEnumerable Containers) + { + Dictionary> searchContainers = null; + + if (Containers != null) + { + searchContainers = Containers + .Where(c => !string.IsNullOrWhiteSpace(c)) + .Distinct() + .Select(c => + { + ADDomain d; + if (TryGetDomainFromDistinguishedName(c, out d)) + return Tuple.Create(d, c); + else + return null; + }).Where(i => i != null) + .GroupBy(i => i.Item1) + .ToDictionary(g => g.Key.Name.ToLower(), g => g.Select(i => i.Item2).ToList()); + } + + if (searchContainers == null || searchContainers.Count == 0) + { + Database.DiscoConfiguration.ActiveDirectory.SearchContainers = null; + ReinitializeSearchContainers(null); + } + else + { + Database.DiscoConfiguration.ActiveDirectory.SearchContainers = searchContainers; + ReinitializeSearchContainers(searchContainers); + } + } + + private void ReinitializeSearchContainers(Dictionary> Containers) + { + if (Containers == null) + { + // No search restrictions (search entire domain) + foreach (var domain in this.Domains) + domain.UpdateSearchEntireDomain(); + } + else + { + // Restrict search containers + var searchContainerDomains = Containers.Join(this.Domains, ok => ok.Key, ik => ik.Name, (o, i) => Tuple.Create(o, i), StringComparer.OrdinalIgnoreCase); + foreach (var domainContainers in searchContainerDomains) + domainContainers.Item2.UpdateSearchContainers(domainContainers.Item1.Value); + + // Ignore domains without configured containers + var unconfiguredContainers = this.Domains.Except(searchContainerDomains.Select(sc => sc.Item2)); + foreach (var domain in unconfiguredContainers) + domain.UpdateSearchContainers(new List()); + } + } + + #endregion + } +} diff --git a/Disco.Services/Interop/ActiveDirectory/ActiveDirectoryExtensions.cs b/Disco.Services/Interop/ActiveDirectory/ActiveDirectoryExtensions.cs index ce196e62..9e19096f 100644 --- a/Disco.Services/Interop/ActiveDirectory/ActiveDirectoryExtensions.cs +++ b/Disco.Services/Interop/ActiveDirectory/ActiveDirectoryExtensions.cs @@ -1,7 +1,4 @@ -using Disco.Models.Interop.ActiveDirectory; -using Disco.Models.Repository; -using Disco.Services.Interop.ActiveDirectory.Internal; -using System; +using System; using System.Collections.Generic; using System.DirectoryServices; using System.DirectoryServices.ActiveDirectory; @@ -16,23 +13,7 @@ namespace Disco.Services.Interop.ActiveDirectory { #region Domain/Directory Extensions - public static DomainController RetrieveWritableDomainController(this ActiveDirectoryDomain domain) - { - return ADInterop.RetrieveWritableDomainController(domain); - } - - public static IEnumerable RetrieveReachableDomainControllers(this ActiveDirectorySite site, ActiveDirectoryDomain domain) - { - return site.Servers.OfType().Where(dc => dc.Reachable() && dc.Domain.Name.Equals(domain.DnsName)); - } - - public static IEnumerable RetrieveReachableDomainControllers(this ActiveDirectoryDomain domain) - { - var d = Domain.GetDomain(new DirectoryContext(DirectoryContextType.Domain, domain.DnsName)); - return d.FindAllDomainControllers().OfType().Where(dc => dc.Reachable()); - } - - public static bool Reachable(this DirectoryServer ds) + public static bool IsReachable(this DirectoryServer ds) { using (Ping p = new Ping()) { @@ -41,351 +22,82 @@ namespace Disco.Services.Interop.ActiveDirectory } } - public static string GetFriendlyOrganisationalUnitName(this ActiveDirectoryDomain domain, string DistinguishedName) + public static IEnumerable WhereReachable(this DomainControllerCollection domainControllers) { - if (!DistinguishedName.EndsWith(domain.DistinguishedName, StringComparison.InvariantCultureIgnoreCase)) - throw new ArgumentException(string.Format("The Distinguished Name [{0}] doesn't exist within this domain [{1}]", DistinguishedName, domain.DistinguishedName)); - - StringBuilder name = new StringBuilder(); - - name.Append('[').Append(domain.NetBiosName).Append(']'); - - var subDN = DistinguishedName.Substring(0, DistinguishedName.Length - domain.DistinguishedName.Length); - var subDNComponents = subDN.Split(','); - - subDNComponents - .Where(c => !string.IsNullOrWhiteSpace(c)) - .Reverse() - .Select(c => c.Substring(c.IndexOf('=') + 1)) - .ToList() - .ForEach(c => name.Append(" > ").Append(c)); - - return name.ToString(); + return domainControllers.Cast().Where(dc => dc.IsReachable()); } - public static string GetDefaultComputerContainer(this ActiveDirectoryDomain domain) + public static IEnumerable WhereReachable(this IEnumerable domainControllers) { - return string.Format("CN=Computers,{0}", domain.DistinguishedName); + return domainControllers.Where(dc => dc.DomainController.IsReachable()); + } + + // Directory Entry Properties (Generic Helpers) + public static T Value(this PropertyCollection properties, string PropertyName) + { + var p = properties.Values(PropertyName); + return p.FirstOrDefault(); + } + public static IEnumerable Values(this PropertyCollection properties, string PropertyName) + { + var p = properties[PropertyName]; + return p.OfType(); } #endregion - #region User Account Extensions - public static object GetPropertyValue(this ActiveDirectoryUserAccount account, string PropertyName, int Index = 0) + #region ADObject Builders + + // User Accounts + public static ADUserAccount AsADUserAccount(this ADSearchResult SearchResult, bool Quick, string[] AdditionalProperties) { - switch (PropertyName.ToLower()) - { - case "name": - return account.Name; - case "samaccountname": - return account.SamAccountName; - case "distinguishedname": - return account.DistinguishedName; - case "objectsid": - return account.SecurityIdentifier; - case "sn": - return account.Surname; - case "givenname": - return account.GivenName; - case "mail": - return account.Email; - case "telephonenumber": - return account.Phone; - default: - object[] adProperty; - if (account.LoadedProperties.TryGetValue(PropertyName, out adProperty) && Index <= adProperty.Length) - return adProperty[Index]; - else - return null; - } + return ADUserAccount.FromSearchResult(SearchResult, Quick, AdditionalProperties); } - #endregion - - #region Machine Account Extensions - - public static void DeleteAccount(this ActiveDirectoryMachineAccount account, DomainController DomainController) + public static IEnumerable AsADUserAccounts(this IEnumerable SearchResults, bool Quick, string[] AdditionalProperties) { - if (account.IsCriticalSystemObject) - throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName)); - - using (DirectoryEntry deAccount = DomainController.RetrieveDirectoryEntry(account.DistinguishedName)) - { - deAccount.DeleteObjectRecursive(); - } - } - public static void DeleteAccount(this ActiveDirectoryMachineAccount account) - { - var domain = account.GetDomain(); - - using (var domainController = domain.RetrieveWritableDomainController()) - { - account.DeleteAccount(domainController); - } + return SearchResults.Select(sr => ADUserAccount.FromSearchResult(sr, Quick, AdditionalProperties)); } - private static void SetNetbootGUID(this ActiveDirectoryMachineAccount account, DomainController DomainController, System.Guid updatedNetbootGUID) + // Machine Accounts + public static ADMachineAccount AsADMachineAccount(this ADSearchResult SearchResult, string[] AdditionalProperties) { - if (account.IsCriticalSystemObject) - throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName)); - - using (DirectoryEntry deAccount = DomainController.RetrieveDirectoryEntry(account.DistinguishedName)) - { - PropertyValueCollection netbootGUIDProp = deAccount.Properties["netbootGUID"]; - bool flag = netbootGUIDProp.Count > 0; - if (flag) - { - netbootGUIDProp.Clear(); - } - netbootGUIDProp.Add(updatedNetbootGUID.ToByteArray()); - deAccount.CommitChanges(); - } + return ADMachineAccount.FromSearchResult(SearchResult, AdditionalProperties); } - public static void SetDescription(this ActiveDirectoryMachineAccount account, DomainController DomainController, string Description) + public static IEnumerable AsADMachineAccounts(this IEnumerable SearchResults, string[] AdditionalProperties) { - using (DirectoryEntry deAccount = DomainController.RetrieveDirectoryEntry(account.DistinguishedName)) - { - PropertyValueCollection descriptionProp = deAccount.Properties["description"]; - if (descriptionProp.Count > 0) - { - descriptionProp.Clear(); - } - if (!string.IsNullOrEmpty(Description)) - { - descriptionProp.Add(Description); - } - deAccount.CommitChanges(); - } - } - public static void SetDescription(this ActiveDirectoryMachineAccount account, string Description) - { - var domain = account.GetDomain(); - - using (var domainController = domain.RetrieveWritableDomainController()) - { - account.SetDescription(domainController, Description); - } + return SearchResults.Select(sr => ADMachineAccount.FromSearchResult(sr, AdditionalProperties)); } - public static void SetDescription(this ActiveDirectoryMachineAccount account, DomainController DomainController, Device Device) + // Groups + public static ADGroup AsADGroup(this ADSearchResult SearchResult) { - System.Text.StringBuilder descriptionBuilder = new System.Text.StringBuilder(); - - if (Device.AssignedUserId != null) - { - descriptionBuilder.Append(Device.AssignedUser.UserId).Append(" (").Append(Device.AssignedUser.DisplayName).Append("); "); - } - - if (Device.DeviceModelId.HasValue) - { - descriptionBuilder.Append(Device.DeviceModel.Description).Append("; "); - } - - descriptionBuilder.Append(Device.DeviceProfile.Description).Append(";"); - - string description = descriptionBuilder.ToString().Trim(); - if (description.Length > 1024) - description = description.Substring(0, 1024); - - account.SetDescription(DomainController, description); + return ADGroup.FromSearchResult(SearchResult); } - public static void SetDescription(this ActiveDirectoryMachineAccount account, Device Device) + public static IEnumerable AsADGroups(this IEnumerable SearchResults) { - var domain = account.GetDomain(); - - using (var domainController = domain.RetrieveWritableDomainController()) - { - account.SetDescription(domainController, Device); - } + return SearchResults.Select(sr => ADGroup.FromSearchResult(sr)); + } + public static ADGroup AsADGroup(this ADDirectoryEntry DirectoryEntry) + { + return ADGroup.FromDirectoryEntry(DirectoryEntry); + } + public static IEnumerable AsADGroups(this IEnumerable DirectoryEntries) + { + return DirectoryEntries.Select(de => ADGroup.FromDirectoryEntry(de)); } - public static void DisableAccount(this ActiveDirectoryMachineAccount account, DomainController DomainController) + // Organisational Units + public static ADOrganisationalUnit AsADOrganisationalUnit(this ADSearchResult SearchResult) { - if (account.IsCriticalSystemObject) - throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName)); - - using (DirectoryEntry deAccount = DomainController.RetrieveDirectoryEntry(account.DistinguishedName)) - { - int accountControl = (int)deAccount.Properties["userAccountControl"][0]; - int updatedAccountControl = (accountControl | 2); - if (accountControl != updatedAccountControl) - { - deAccount.Properties["userAccountControl"][0] = updatedAccountControl; - deAccount.CommitChanges(); - } - } + return ADOrganisationalUnit.FromSearchResult(SearchResult); } - public static void DisableAccount(this ActiveDirectoryMachineAccount account) + public static IEnumerable AsADOrganisationalUnit(this IEnumerable SearchResults) { - var domain = account.GetDomain(); - - using (var domainController = domain.RetrieveWritableDomainController()) - { - account.DisableAccount(domainController); - } - } - public static void EnableAccount(this ActiveDirectoryMachineAccount account, DomainController DomainController) - { - if (account.IsCriticalSystemObject) - throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName)); - - using (DirectoryEntry deAccount = DomainController.RetrieveDirectoryEntry(account.DistinguishedName)) - { - int accountControl = (int)deAccount.Properties["userAccountControl"][0]; - if ((accountControl & 2) == 2) - { - int updatedAccountControl = (accountControl ^ 2); - deAccount.Properties["userAccountControl"][0] = updatedAccountControl; - deAccount.CommitChanges(); - } - } - } - public static void EnableAccount(this ActiveDirectoryMachineAccount account) - { - var domain = account.GetDomain(); - - using (var domainController = domain.RetrieveWritableDomainController()) - { - account.EnableAccount(domainController); - } - } - - public static bool UpdateNetbootGUID(this ActiveDirectoryMachineAccount account, DomainController DomainController, string UUID, string MACAddress) - { - if (account.IsCriticalSystemObject) - throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName)); - - Guid netbootGUID = Guid.Empty; - - if (!string.IsNullOrWhiteSpace(UUID)) - { - netbootGUID = ActiveDirectoryExtensions.NetbootGUIDFromUUID(UUID); - } - else if (!string.IsNullOrWhiteSpace(MACAddress)) - { - netbootGUID = ActiveDirectoryExtensions.NetbootGUIDFromMACAddress(MACAddress); - } - - if (netbootGUID != System.Guid.Empty && netbootGUID != account.NetbootGUID) - { - account.SetNetbootGUID(DomainController, netbootGUID); - return true; - } - else - { - return false; - } - } - public static bool UpdateNetbootGUID(this ActiveDirectoryMachineAccount account, string UUID, string MACAddress) - { - var domain = account.GetDomain(); - - using (var domainController = domain.RetrieveWritableDomainController()) - { - return account.UpdateNetbootGUID(domainController, UUID, MACAddress); - } - } - public static System.Guid NetbootGUIDFromMACAddress(string MACAddress) - { - string strippedMACAddress = MACAddress.Trim().Replace(":", string.Empty).Replace("-", string.Empty); - bool flag = strippedMACAddress.Length == 12; - System.Guid NetbootGUIDFromMACAddress; - if (flag) - { - System.Guid guid = new System.Guid(string.Format("00000000-0000-0000-0000-{0}", strippedMACAddress)); - NetbootGUIDFromMACAddress = guid; - } - else - { - NetbootGUIDFromMACAddress = System.Guid.Empty; - } - return NetbootGUIDFromMACAddress; - } - public static System.Guid NetbootGUIDFromUUID(string UUID) - { - System.Guid result = new System.Guid(UUID); - return result; - } - - public static object GetPropertyValue(this ActiveDirectoryMachineAccount account, string PropertyName, int Index = 0) - { - switch (PropertyName.ToLower()) - { - case "name": - return account.Name; - case "samaccountname": - return account.SamAccountName; - case "distinguishedname": - return account.DistinguishedName; - case "objectsid": - return account.SecurityIdentifier; - case "netbootguid": - return account.NetbootGUID; - default: - object[] adProperty; - if (account.LoadedProperties.TryGetValue(PropertyName, out adProperty) && Index <= adProperty.Length) - return adProperty[Index]; - else - return null; - } - } - - public static IPStatus PingComputer(this ActiveDirectoryMachineAccount account, int Timeout = 2000) - { - using (var p = new Ping()) - { - PingReply reply = p.Send(account.DnsName, Timeout); - return reply.Status; - } - } - - public static void MoveOrganisationalUnit(this ActiveDirectoryMachineAccount account, DomainController DomainController, string NewOrganisationUnit) - { - if (account.IsCriticalSystemObject) - throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName)); - - var parentDistinguishedName = account.ParentDistinguishedName(); - - if (parentDistinguishedName != null && !parentDistinguishedName.Equals(NewOrganisationUnit, StringComparison.InvariantCultureIgnoreCase)) - { - var domain = account.GetDomain(); - - // If no OU provided, place in default Computers container - if (string.IsNullOrWhiteSpace(NewOrganisationUnit)) - NewOrganisationUnit = domain.GetDefaultComputerContainer(); - - if (!NewOrganisationUnit.EndsWith(domain.DistinguishedName, StringComparison.InvariantCultureIgnoreCase)) - throw new InvalidOperationException(string.Format("Unable to move AD Account from one domain [{0}] to another [{1}].", account.DistinguishedName, NewOrganisationUnit)); - - using (DirectoryEntry ou = DomainController.RetrieveDirectoryEntry(NewOrganisationUnit)) - { - using (DirectoryEntry i = DomainController.RetrieveDirectoryEntry(account.DistinguishedName)) - { - i.UsePropertyCache = false; - i.MoveTo(ou); - } - } - } - } - - public static string ParentDistinguishedName(this ActiveDirectoryMachineAccount account) - { - // Determine Parent - if (!string.IsNullOrWhiteSpace(account.DistinguishedName)) - return account.DistinguishedName.Substring(account.DistinguishedName.IndexOf(",") + 1); - else - return null; - } - - public static ActiveDirectoryDomain GetDomain(this ActiveDirectoryMachineAccount account) - { - var domain = ActiveDirectory.GetDomainByNetBiosName(account.Domain); - - if (domain == null) - throw new InvalidOperationException(string.Format("Unable to find Domain [{0}] for account [{1}]", account.Domain, account.Name)); - else - return domain; + return SearchResults.Select(sr => ADOrganisationalUnit.FromSearchResult(sr)); } #endregion + + } } diff --git a/Disco.Services/Interop/ActiveDirectory/ActiveDirectoryGroupCache.cs b/Disco.Services/Interop/ActiveDirectory/ActiveDirectoryGroupCache.cs new file mode 100644 index 00000000..bc5688d4 --- /dev/null +++ b/Disco.Services/Interop/ActiveDirectory/ActiveDirectoryGroupCache.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Security.Principal; +using System.Text; +using System.Threading.Tasks; + +namespace Disco.Services.Interop.ActiveDirectory +{ + public class ActiveDirectoryGroupCache + { + private ConcurrentDictionary> securityIdentifierCache; + private ConcurrentDictionary> distinguishedNameCache; + private const long CacheTimeoutTicks = 6000000000; // 10 Minutes + + private const int CacheCleanIntervalMinutes = 15; + private DateTime cacheCleanNext; + private object cacheCleanLock = new object(); + private Task cacheCleanTask; + + public ActiveDirectoryGroupCache() + { + this.securityIdentifierCache = new ConcurrentDictionary>(); + this.distinguishedNameCache = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); + cacheCleanNext = DateTime.Now.AddMinutes(CacheCleanIntervalMinutes); + } + + public ADGroup GetGroup(string DistinguishedName) + { + // Check Cache + Tuple groupRecord = TryDistinguishedNameCache(DistinguishedName); + + if (groupRecord == null) + { + // Load from AD + var group = ActiveDirectory.RetrieveADGroupByDistinguishedName(DistinguishedName); + SetValue(group); + + return group; + } + else + { + // Return from Cache + return groupRecord.Item1; + } + } + public ADGroup GetGroup(SecurityIdentifier SecurityIdentifier) + { + // Check Cache + Tuple groupRecord = TrySecurityIdentifierCache(SecurityIdentifier); + + if (groupRecord == null) + { + // Load from AD + var group = ActiveDirectory.RetrieveADGroupWithSecurityIdentifier(SecurityIdentifier); + SetValue(group); + + return group; + } + else + { + // Return from Cache + return groupRecord.Item1; + } + } + + public IEnumerable GetRecursiveGroups(IEnumerable DistinguishedNames) + { + List groups = new List(); + + foreach (var distinguishedName in DistinguishedNames) + foreach (var group in GetGroupsRecursive(distinguishedName, new Stack())) + if (!groups.Contains(group)) + { + groups.Add(group); + yield return group; + } + } + public IEnumerable GetRecursiveGroups(string DistinguishedName) + { + foreach (var group in GetGroupsRecursive(DistinguishedName, new Stack())) + yield return group; + } + private IEnumerable GetGroupsRecursive(string DistinguishedName, Stack RecursiveTree) + { + var group = GetGroup(DistinguishedName); + + if (group != null && !RecursiveTree.Contains(group)) + { + yield return group; + + if (group.MemberOf != null) + { + RecursiveTree.Push(group); + + foreach (var parentDistinguishedName in group.MemberOf) + foreach (var parentGroup in GetGroupsRecursive(parentDistinguishedName, RecursiveTree)) + yield return parentGroup; + + RecursiveTree.Pop(); + } + } + } + + private Tuple TryDistinguishedNameCache(string DistinguishedName) + { + Tuple groupRecord; + if (distinguishedNameCache.TryGetValue(DistinguishedName, out groupRecord)) + { + if (groupRecord.Item2 > DateTime.Now) + return groupRecord; + else + { + if (distinguishedNameCache.TryRemove(DistinguishedName, out groupRecord)) + securityIdentifierCache.TryRemove(groupRecord.Item1.SecurityIdentifier, out groupRecord); + } + } + return null; + } + private Tuple TrySecurityIdentifierCache(SecurityIdentifier SecurityIdentifier) + { + Tuple groupRecord; + if (securityIdentifierCache.TryGetValue(SecurityIdentifier, out groupRecord)) + { + if (groupRecord.Item2 > DateTime.Now) + return groupRecord; + else + { + if (securityIdentifierCache.TryRemove(SecurityIdentifier, out groupRecord)) + distinguishedNameCache.TryRemove(groupRecord.Item1.DistinguishedName, out groupRecord); + } + } + return null; + } + + private bool SetValue(ADGroup Group) + { + Tuple groupRecord = Tuple.Create(Group, DateTime.Now.AddTicks(CacheTimeoutTicks)); + Tuple oldGroupRecord; + + var distinguishedName = Group.DistinguishedName; + var securityIdentifier = Group.SecurityIdentifier; + + if (distinguishedNameCache.ContainsKey(distinguishedName)) + { + if (distinguishedNameCache.TryGetValue(distinguishedName, out oldGroupRecord)) + { + distinguishedNameCache.TryUpdate(distinguishedName, groupRecord, oldGroupRecord); + } + } + else + { + distinguishedNameCache.TryAdd(distinguishedName, groupRecord); + } + + if (securityIdentifierCache.ContainsKey(securityIdentifier)) + { + if (securityIdentifierCache.TryGetValue(securityIdentifier, out oldGroupRecord)) + { + securityIdentifierCache.TryUpdate(securityIdentifier, groupRecord, oldGroupRecord); + } + } + else + { + securityIdentifierCache.TryAdd(securityIdentifier, groupRecord); + } + return true; + } + + #region Stale Cache Clean + + private void EnsureCleanCache() + { + if (cacheCleanTask == null && cacheCleanNext < DateTime.Now) + { + lock (cacheCleanLock) + { + if (cacheCleanTask == null && cacheCleanNext < DateTime.Now) + { + cacheCleanTask = Task.Factory.StartNew(CleanCache); + } + } + } + } + + private void CleanCache() + { + DateTime now = DateTime.Now; + + // Clean Cache + var dnKeys = distinguishedNameCache.Keys.ToArray(); + foreach (var dnKey in dnKeys) + { + Tuple groupRecord; + if (distinguishedNameCache.TryGetValue(dnKey, out groupRecord)) + { + if (groupRecord.Item2 <= now) + { + distinguishedNameCache.TryRemove(dnKey, out groupRecord); + } + } + } + + // Clean SID Cache + var siKeys = securityIdentifierCache.Keys.ToArray(); + foreach (var siKey in siKeys) + { + Tuple groupRecord; + if (securityIdentifierCache.TryGetValue(siKey, out groupRecord)) + { + if (groupRecord.Item2 <= now) + { + securityIdentifierCache.TryRemove(siKey, out groupRecord); + } + } + } + + // Schedule Next Clean + cacheCleanNext = now.AddMinutes(CacheCleanIntervalMinutes); + cacheCleanTask = null; + } + + #endregion + } +} diff --git a/Disco.Services/Interop/ActiveDirectory/IADObject.cs b/Disco.Services/Interop/ActiveDirectory/IADObject.cs new file mode 100644 index 00000000..b74e7f2b --- /dev/null +++ b/Disco.Services/Interop/ActiveDirectory/IADObject.cs @@ -0,0 +1,18 @@ + +using System.Security.Principal; +namespace Disco.Services.Interop.ActiveDirectory +{ + public interface IADObject + { + ADDomain Domain { get; } + + string DistinguishedName { get; } + SecurityIdentifier SecurityIdentifier { get; } + + string Id { get; } + string SamAccountName { get; } + + string Name { get; } + string DisplayName { get; } + } +} diff --git a/Disco.Services/Interop/ActiveDirectory/Internal/ADGroupCache.cs b/Disco.Services/Interop/ActiveDirectory/Internal/ADGroupCache.cs deleted file mode 100644 index 216b368a..00000000 --- a/Disco.Services/Interop/ActiveDirectory/Internal/ADGroupCache.cs +++ /dev/null @@ -1,226 +0,0 @@ -using Disco.Data.Repository; -using Disco.Models.Interop.ActiveDirectory; -using Disco.Services.Tasks; -using Quartz; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; - -namespace Disco.Services.Interop.ActiveDirectory.Internal -{ - public class ADGroupCache : ScheduledTask - { - private static ConcurrentDictionary> _SecurityIdentifierCache = new ConcurrentDictionary>(); - private static ConcurrentDictionary> _DistinguishedNameCache = new ConcurrentDictionary>(); - private const long CacheTimeoutTicks = 6000000000; // 10 Minutes - - public static IEnumerable GetGroups(IEnumerable DistinguishedNames) - { - List groups = new List(); - - foreach (var distinguishedName in DistinguishedNames) - foreach (var group in GetGroupsRecursive(distinguishedName, new Stack())) - if (!groups.Contains(group)) - { - groups.Add(group); - yield return group.NetBiosId; - } - } - public static IEnumerable GetGroups(string DistinguishedName) - { - foreach (var group in GetGroupsRecursive(DistinguishedName, new Stack())) - yield return group.NetBiosId; - } - public static string GetGroupsDistinguishedNameForSecurityIdentifier(string SecurityIdentifier) - { - var group = GetGroupBySecurityIdentifier(SecurityIdentifier); - if (group == null) - return null; - else - return group.DistinguishedName; - } - private static IEnumerable GetGroupsRecursive(string DistinguishedName, Stack RecursiveTree) - { - var group = GetGroup(DistinguishedName); - - if (group != null && !RecursiveTree.Contains(group)) - { - yield return group; - - if (group.MemberOf != null) - { - RecursiveTree.Push(group); - - foreach (var parentDistinguishedName in group.MemberOf) - foreach (var parentGroup in GetGroupsRecursive(parentDistinguishedName, RecursiveTree)) - yield return parentGroup; - - RecursiveTree.Pop(); - } - } - } - - private static ActiveDirectoryGroup GetGroup(string DistinguishedName) - { - // Check Cache - Tuple groupRecord = TryCache(DistinguishedName); - - if (groupRecord == null) - { - // Load from AD - var group = ActiveDirectory.RetrieveGroupWithDistinguishedName(DistinguishedName); - SetValue(group); - - return group; - } - else - { - // Return from Cache - return groupRecord.Item1; - } - } - private static ActiveDirectoryGroup GetGroupBySecurityIdentifier(string SecurityIdentifier) - { - // Check Cache - Tuple groupRecord = TrySecurityIdentifierCache(SecurityIdentifier); - - if (groupRecord == null) - { - // Load from AD - var group = ActiveDirectory.RetrieveGroupWithSecurityIdentifier(SecurityIdentifier); - SetValue(group); - - return group; - } - else - { - // Return from Cache - return groupRecord.Item1; - } - } - - private static Tuple TryCache(string DistinguishedName) - { - string distinguishedName = DistinguishedName.ToLower(); - Tuple groupRecord; - if (_DistinguishedNameCache.TryGetValue(distinguishedName, out groupRecord)) - { - if (groupRecord.Item2 > DateTime.Now) - return groupRecord; - else - { - if (_DistinguishedNameCache.TryRemove(distinguishedName, out groupRecord)) - _SecurityIdentifierCache.TryRemove(groupRecord.Item1.SecurityIdentifier, out groupRecord); - } - } - return null; - } - private static Tuple TrySecurityIdentifierCache(string SecurityIdentifier) - { - Tuple groupRecord; - if (_SecurityIdentifierCache.TryGetValue(SecurityIdentifier, out groupRecord)) - { - if (groupRecord.Item2 > DateTime.Now) - return groupRecord; - else - { - if (_SecurityIdentifierCache.TryRemove(SecurityIdentifier, out groupRecord)) - _DistinguishedNameCache.TryRemove(groupRecord.Item1.DistinguishedName.ToLower(), out groupRecord); - } - } - return null; - } - private static bool SetValue(ActiveDirectoryGroup Group) - { - Tuple groupRecord = new Tuple(Group, DateTime.Now.AddTicks(CacheTimeoutTicks)); - Tuple oldGroupRecord; - - string key = Group.DistinguishedName.ToLower(); - if (_DistinguishedNameCache.ContainsKey(key)) - { - if (_DistinguishedNameCache.TryGetValue(key, out oldGroupRecord)) - { - _DistinguishedNameCache.TryUpdate(key, groupRecord, oldGroupRecord); - } - } - else - { - _DistinguishedNameCache.TryAdd(key, groupRecord); - } - - string securityIdentifier = Group.SecurityIdentifier; - if (_SecurityIdentifierCache.ContainsKey(securityIdentifier)) - { - if (_SecurityIdentifierCache.TryGetValue(securityIdentifier, out oldGroupRecord)) - { - _SecurityIdentifierCache.TryUpdate(securityIdentifier, groupRecord, oldGroupRecord); - } - } - else - { - _SecurityIdentifierCache.TryAdd(securityIdentifier, groupRecord); - } - return true; - } - - private static void CleanStaleCache() - { - // Clean Cache - var groupKeys = _DistinguishedNameCache.Keys.ToArray(); - foreach (string groupKey in groupKeys) - { - Tuple groupRecord; - if (_DistinguishedNameCache.TryGetValue(groupKey, out groupRecord)) - { - if (groupRecord.Item2 <= DateTime.Now) - { - _DistinguishedNameCache.TryRemove(groupKey, out groupRecord); - } - } - } - - // Clean SID Cache - groupKeys = _SecurityIdentifierCache.Keys.ToArray(); - foreach (string groupKey in groupKeys) - { - Tuple groupRecord; - if (_SecurityIdentifierCache.TryGetValue(groupKey, out groupRecord)) - { - if (groupRecord.Item2 <= DateTime.Now) - { - _SecurityIdentifierCache.TryRemove(groupKey, out groupRecord); - } - } - } - } - - public override string TaskName { get { return "AD Group Cache - Clean Stale Cache"; } } - - public override bool SingleInstanceTask { get { return true; } } - public override bool CancelInitiallySupported { get { return false; } } - public override bool LogExceptionsOnly { get { return true; } } - - public override void InitalizeScheduledTask(DiscoDataContext Database) - { - // Run @ every 15mins - - // Next 15min interval - DateTime now = DateTime.Now; - int mins = (15 - (now.Minute % 15)); - if (mins < 10) - mins += 15; - DateTimeOffset startAt = new DateTimeOffset(now).AddMinutes(mins).AddSeconds(now.Second * -1).AddMilliseconds(now.Millisecond * -1); - - TriggerBuilder triggerBuilder = TriggerBuilder.Create().StartAt(startAt). - WithSchedule(SimpleScheduleBuilder.RepeatMinutelyForever(15)); - - this.ScheduleTask(triggerBuilder); - } - - protected override void ExecuteTask() - { - CleanStaleCache(); - } - } -} diff --git a/Disco.Services/Interop/ActiveDirectory/Internal/ADInterop.cs b/Disco.Services/Interop/ActiveDirectory/Internal/ADInterop.cs deleted file mode 100644 index 98c7b201..00000000 --- a/Disco.Services/Interop/ActiveDirectory/Internal/ADInterop.cs +++ /dev/null @@ -1,682 +0,0 @@ -using Disco.Data.Repository; -using Disco.Models.Interop.ActiveDirectory; -using Disco.Services.Tasks; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.DirectoryServices; -using System.DirectoryServices.ActiveDirectory; -using System.Linq; -using System.Security.Principal; -using System.Text; -using System.Threading.Tasks; - -namespace Disco.Services.Interop.ActiveDirectory.Internal -{ - internal static class ADInterop - { - public static List Domains { get; private set; } - public static ActiveDirectoryDomain PrimaryDomain { get; private set; } - public static ActiveDirectorySite Site { get; private set; } - internal static List _ForestServers { get; set; } - private static bool _SearchEntireForest { get; set; } - private static bool _Initialized = false; - private static object _InitializeLock = new object(); - - #region Initialization - - public static void Initialize(DiscoDataContext Database) - { - if (!_Initialized) - { - lock (_InitializeLock) - { - if (!_Initialized) - { - _SearchEntireForest = Database.DiscoConfiguration.ActiveDirectory.SearchEntireForest ?? true; // Default True - - using (var computerDomain = Domain.GetComputerDomain()) - { - PrimaryDomain = GetDomainInternal(computerDomain, Database); - - Domains = computerDomain.Forest.Domains.Cast().Select(d => - { - if (d.Name == PrimaryDomain.DnsName) - return PrimaryDomain; - else - return GetDomainInternal(d, Database); - }).ToList(); - } - - Site = ActiveDirectorySite.GetComputerSite(); - } - } - } - } - - private static ActiveDirectoryDomain GetDomainInternal(Domain d, DiscoDataContext Database) - { - string ldapPath = string.Format("LDAP://{0}/", d.Name); - string defaultNamingContext; - string configurationNamingContext; - string netBiosName; - - using (var adRootDSE = new DirectoryEntry(ldapPath + "RootDSE")) - { - defaultNamingContext = adRootDSE.Properties["defaultNamingContext"][0].ToString(); - configurationNamingContext = adRootDSE.Properties["configurationNamingContext"][0].ToString(); - } - - using (var configSearchRoot = new DirectoryEntry(ldapPath + "CN=Partitions," + configurationNamingContext)) - { - var configSearchFilter = string.Format("(&(objectcategory=Crossref)(dnsRoot={0})(netBIOSName=*))", d.Name); - var configSearchLoadProperites = new string[] { "NetBIOSName" }; - - using (var configSearcher = new DirectorySearcher(configSearchRoot, configSearchFilter, configSearchLoadProperites, System.DirectoryServices.SearchScope.OneLevel)) - { - SearchResult configResult = configSearcher.FindOne(); - - if (configResult != null) - netBiosName = configResult.Properties["NetBIOSName"][0].ToString(); - else - netBiosName = null; - } - } - - // Search Containers - var searchContainersAll = Database.DiscoConfiguration.ActiveDirectory.SearchContainers; - List searchContainers = null; - - if (searchContainersAll == null || searchContainersAll.Count == 0 || !searchContainersAll.TryGetValue(d.Name.ToLower(), out searchContainers)) - searchContainers = new List() { defaultNamingContext }; // No search constraints set - search entire tree - - return new ActiveDirectoryDomain(d.Name, netBiosName, defaultNamingContext, searchContainers); - } - - public static void UpdateSearchContainers(DiscoDataContext Database, IEnumerable Containers) - { - Dictionary> searchContainers = null; - - if (Containers != null) - { - searchContainers = Containers - .Where(c => !string.IsNullOrWhiteSpace(c)) - .Distinct() - .Select(c => - { - ActiveDirectoryDomain d; - if (TryGetDomainByDistinguishedName(c, out d)) - return Tuple.Create(d, c); - else - return null; - }).Where(i => i != null) - .GroupBy(i => i.Item1) - .ToDictionary(g => g.Key.DnsName.ToLower(), g => g.Select(i => i.Item2).ToList()); - } - - if (searchContainers == null || searchContainers.Count == 0) - { - Database.DiscoConfiguration.ActiveDirectory.SearchContainers = null; - - // No search constraints set - search entire tree - Domains.ForEach(d => d.UpdateSearchContainers(new string[] { d.DistinguishedName })); - } - else - { - Database.DiscoConfiguration.ActiveDirectory.SearchContainers = searchContainers; - - Domains.ForEach(d => - { - List domainContainers; - if (searchContainers.TryGetValue(d.DnsName.ToLower(), out domainContainers)) - d.UpdateSearchContainers(domainContainers); - else - d.UpdateSearchContainers(Enumerable.Empty()); - }); - } - } - - public static bool UpdateSearchEntireForest(DiscoDataContext Database, bool SearchEntireForest) - { - if (SearchEntireForest == false) - { - Database.DiscoConfiguration.ActiveDirectory.SearchEntireForest = false; - ADInterop._SearchEntireForest = false; - return true; - } - else - { - var forestServers = LoadForestServers(); - if (forestServers.Count <= ActiveDirectory.MaxForestServerSearch) - { - Database.DiscoConfiguration.ActiveDirectory.SearchEntireForest = true; - ADInterop._SearchEntireForest = true; - return true; - } - else - { - Database.DiscoConfiguration.ActiveDirectory.SearchEntireForest = false; - ADInterop._SearchEntireForest = false; - return false; - } - } - } - - #endregion - - #region Domain Getters - - public static bool TryGetDomainByDistinguishedName(string DistinguishedName, out ActiveDirectoryDomain Domain) - { - // Find closest match - Domain = ADInterop.Domains.Where(d => DistinguishedName.EndsWith(d.DistinguishedName, StringComparison.InvariantCultureIgnoreCase)) - .OrderByDescending(d => d.DistinguishedName.Length).FirstOrDefault(); - - return (Domain != null); - } - public static ActiveDirectoryDomain GetDomainByDistinguishedName(string DistinguishedName) - { - ActiveDirectoryDomain domain; - if (!TryGetDomainByDistinguishedName(DistinguishedName, out domain)) - throw new ArgumentException(string.Format("The domain is unknown distinguished name: [{0}]", DistinguishedName), "Id"); - return domain; - } - - public static bool TryGetDomainByNetBiosName(string NetBiosName, out ActiveDirectoryDomain Domain) - { - Domain = ADInterop.Domains.FirstOrDefault(d => d.NetBiosName.Equals(NetBiosName, StringComparison.InvariantCultureIgnoreCase)); - return (Domain != null); - } - public static ActiveDirectoryDomain GetDomainByNetBiosName(string NetBiosName) - { - ActiveDirectoryDomain domain; - if (!TryGetDomainByNetBiosName(NetBiosName, out domain)) - throw new ArgumentException(string.Format("The specified domain is unknown [{0}]", NetBiosName), "Id"); - return domain; - } - - public static bool TryGetDomainByDnsName(string DnsName, out ActiveDirectoryDomain Domain) - { - Domain = ADInterop.Domains.FirstOrDefault(d => d.DnsName.Equals(DnsName, StringComparison.InvariantCultureIgnoreCase)); - return (Domain != null); - } - public static ActiveDirectoryDomain GetDomainByDnsName(string DnsName) - { - ActiveDirectoryDomain domain; - if (!TryGetDomainByDnsName(DnsName, out domain)) - throw new ArgumentException(string.Format("The specified domain is unknown [{0}]", DnsName), "Id"); - return domain; - } - - public static bool TryGetDomainFromId(string Id, out ActiveDirectoryDomain Domain) - { - if (string.IsNullOrWhiteSpace(Id)) - throw new ArgumentNullException("Id"); - - var idSplit = UserExtensions.SplitUserId(Id); - - if (idSplit.Item1 == null) - throw new ArgumentException(string.Format("The Id must include the Domain [{0}]", Id), "Id"); - - if (string.IsNullOrWhiteSpace(idSplit.Item1)) - throw new ArgumentException(string.Format("The Id Domain was empty [{0}]", Id), "Id"); - if (string.IsNullOrWhiteSpace(idSplit.Item2)) - throw new ArgumentException(string.Format("The Id Name was empty [{0}]", Id), "Id"); - - return TryGetDomainByNetBiosName(idSplit.Item1, out Domain); - } - public static ActiveDirectoryDomain GetDomainFromId(string Id) - { - if (string.IsNullOrWhiteSpace(Id)) - throw new ArgumentNullException("Id"); - - var idSplit = UserExtensions.SplitUserId(Id); - - if (idSplit.Item1 == null) - throw new ArgumentException(string.Format("The Id must include the Domain [{0}]", Id), "Id"); - - if (string.IsNullOrWhiteSpace(idSplit.Item1)) - throw new ArgumentException(string.Format("The Id Domain was empty [{0}]", Id), "Id"); - if (string.IsNullOrWhiteSpace(idSplit.Item2)) - throw new ArgumentException(string.Format("The Id Name was empty [{0}]", Id), "Id"); - - return GetDomainByNetBiosName(idSplit.Item1); - } - - public static List LoadForestServers() - { - if (_ForestServers == null) - { - lock (_InitializeLock) - { - if (_ForestServers == null) - { - var status = ADDiscoverForestServers.ScheduleNow(); - status.CompletionTask.Wait(); - } - } - } - return _ForestServers; - } - public static Task> LoadForestServersAsync() - { - if (_ForestServers != null) - return Task.FromResult(_ForestServers); - - lock (_InitializeLock) - { - if (_ForestServers != null) - return Task.FromResult(_ForestServers); - - // Invoke Scheduled Task - var status = ADDiscoverForestServers.ScheduleNow(); - - return status.CompletionTask.ContinueWith(t => - { - return ADInterop._ForestServers.ToList(); - }); - } - } - - public static bool SearchEntireForest - { - get - { - if (_ForestServers != null && _ForestServers.Count > ActiveDirectory.MaxForestServerSearch) - return false; // Never - - return _SearchEntireForest; - } - } - - #endregion - - #region Searching - - public static IEnumerable SearchAll(string LdapFilter, string[] LoadProperties) - { - return SearchAll(Domains, LdapFilter, LoadProperties); - } - public static IEnumerable SearchAll(string LdapFilter, int ResultLimit, string[] LoadProperties) - { - return SearchAll(Domains, LdapFilter, ResultLimit, LoadProperties); - } - public static IEnumerable SearchAll(IEnumerable> DomainsWithController, string LdapFilter, string[] LoadProperties) - { - return SearchAll(DomainsWithController, LdapFilter, null, LoadProperties); - } - public static IEnumerable SearchAll(IEnumerable Domains, string LdapFilter, string[] LoadProperties) - { - return SearchAll(Domains, LdapFilter, null, LoadProperties); - } - public static IEnumerable SearchAll(ActiveDirectoryDomain Domain, DomainController DomainController, string LdapFilter, string[] LoadProperties) - { - return SearchAll(Domain, DomainController, LdapFilter, null, LoadProperties); - } - public static IEnumerable SearchAll(ActiveDirectoryDomain Domain, string LdapFilter, string[] LoadProperties) - { - return SearchAll(Domain, LdapFilter, null, LoadProperties); - } - public static IEnumerable SearchAll(IEnumerable Domains, string LdapFilter, int? ResultLimit, string[] LoadProperties) - { - if (Domains == null || Domains.Count() == 0) - return Enumerable.Empty(); - - var queries = Domains.Select(d => Tuple.Create(d, (DomainController)null)).ToList(); - - return SearchAll(queries, LdapFilter, ResultLimit, LoadProperties); - } - public static IEnumerable SearchAll(IEnumerable> DomainsWithController, string LdapFilter, int? ResultLimit, string[] LoadProperties) - { - var queries = DomainsWithController.ToList(); - - IEnumerable results; - - switch (queries.Count) - { - case 0: - results = Enumerable.Empty(); - break; - case 1: - var singleQuery = queries.First(); - results = SearchDomain(singleQuery.Item1, singleQuery.Item2, null, LdapFilter, ResultLimit, LoadProperties); - break; - default: - var taskFactory = new TaskFactory>(); - var tasks = queries - .Select(query => - taskFactory.StartNew(() => - SearchDomain(query.Item1, query.Item2, null, LdapFilter, ResultLimit, LoadProperties)) - ).ToArray(); - Task.WaitAll(tasks); - results = tasks.SelectMany(t => t.Result); - break; - } - - if (ResultLimit.HasValue) - results = results.Take(ResultLimit.Value); - - return results.ToList(); - } - public static IEnumerable SearchAll(ActiveDirectoryDomain Domain, string LdapFilter, int? ResultLimit, string[] LoadProperties) - { - return SearchAll(Domain, null, LdapFilter, ResultLimit, LoadProperties); - } - public static IEnumerable SearchAll(ActiveDirectoryDomain Domain, DomainController DomainController, string LdapFilter, int? ResultLimit, string[] LoadProperties) - { - return SearchDomain(Domain, DomainController, null, LdapFilter, ResultLimit, LoadProperties); - } - - public static IEnumerable SearchScope(string LdapFilter, string[] LoadProperties) - { - return SearchScope(Domains, LdapFilter, LoadProperties); - } - public static IEnumerable SearchScope(string LdapFilter, int ResultLimit, string[] LoadProperties) - { - return SearchScope(Domains, LdapFilter, ResultLimit, LoadProperties); - } - public static IEnumerable SearchScope(IEnumerable Domains, string LdapFilter, string[] LoadProperties) - { - return SearchScope(Domains, LdapFilter, null, LoadProperties); - } - public static IEnumerable SearchScope(IEnumerable> DomainsWithController, string LdapFilter, string[] LoadProperties) - { - return SearchScope(DomainsWithController, LdapFilter, null, LoadProperties); - } - public static IEnumerable SearchScope(ActiveDirectoryDomain Domain, string LdapFilter, string[] LoadProperties) - { - return SearchScope(Domain, LdapFilter, null, LoadProperties); - } - public static IEnumerable SearchScope(ActiveDirectoryDomain Domain, DomainController DomainController, string LdapFilter, string[] LoadProperties) - { - return SearchScope(Domain, DomainController, LdapFilter, null, LoadProperties); - } - public static IEnumerable SearchScope(IEnumerable Domains, string LdapFilter, int? ResultLimit, string[] LoadProperties) - { - return SearchScope(Domains.Select(d => Tuple.Create(d, (DomainController)null)), LdapFilter, ResultLimit, LoadProperties); - } - public static IEnumerable SearchScope(ActiveDirectoryDomain Domain, string LdapFilter, int? ResultLimit, string[] LoadProperties) - { - return SearchScope(Domain, null, LdapFilter, ResultLimit, LoadProperties); - } - public static IEnumerable SearchScope(ActiveDirectoryDomain Domain, DomainController DomainController, string LdapFilter, int? ResultLimit, string[] LoadProperties) - { - if (Domain.SearchContainers == null || Domain.SearchContainers.Count == 0) - return Enumerable.Empty(); - - var query = new List>() { - Tuple.Create(Domain, DomainController) - }; - - return SearchScope(query, LdapFilter, ResultLimit, LoadProperties); - } - public static IEnumerable SearchScope(IEnumerable> DomainsWithController, string LdapFilter, int? ResultLimit, string[] LoadProperties) - { - var queries = DomainsWithController.SelectMany(d => d.Item1.SearchContainers, (d, sc) => Tuple.Create(d.Item1, d.Item2, sc)).ToList(); - - IEnumerable results; - - switch (queries.Count) - { - case 0: - results = Enumerable.Empty(); - break; - case 1: - var singleQuery = queries.First(); - results = SearchDomain(singleQuery.Item1, singleQuery.Item2, singleQuery.Item3, LdapFilter, ResultLimit, LoadProperties); - break; - default: - var taskFactory = new TaskFactory>(); - var tasks = queries - .Select(query => - taskFactory.StartNew(() => - SearchDomain(query.Item1, query.Item2, query.Item3, LdapFilter, ResultLimit, LoadProperties).ToList()) - ).ToArray(); - Task.WaitAll(tasks); - results = tasks.SelectMany(t => t.Result); - break; - } - - if (ResultLimit.HasValue) - results = results.Take(ResultLimit.Value); - - return results.ToList(); - } - - private static IEnumerable SearchDomain(ActiveDirectoryDomain Domain, DomainController DomainController, string SearchRoot, string LdapFilter, int? ResultLimit, string[] LoadProperties) - { - string ldapServer; - if (DomainController != null) - { - ldapServer = DomainController.Name; - } - else - { - var domainDC = Site.RetrieveReachableDomainControllers(Domain).FirstOrDefault(); - if (domainDC != null) - ldapServer = domainDC.Name; - else - ldapServer = Domain.DnsName; - } - string searchRoot = SearchRoot ?? Domain.DistinguishedName; - string ldapPath = string.Format(@"LDAP://{0}/{1}", ldapServer, searchRoot); - - using (DirectoryEntry rootEntry = new DirectoryEntry(ldapPath)) - { - using (DirectorySearcher searcher = new DirectorySearcher(rootEntry, LdapFilter, LoadProperties, System.DirectoryServices.SearchScope.Subtree)) - { - searcher.PageSize = 500; - - if (ResultLimit.HasValue) - searcher.SizeLimit = ResultLimit.Value; - return searcher.FindAll().Cast().Select(result => new ActiveDirectorySearchResult() - { - Domain = Domain, - SearchRoot = searchRoot, - Result = result, - }); - } - } - } - - #endregion - - public static string OfflineDomainJoinProvision(ActiveDirectoryDomain Domain, DomainController DomainController, string ComputerSamAccountName, string OrganisationalUnit, out string DiagnosticInformation) - { - StringBuilder diagnosticInfo = new StringBuilder(); - string DJoinResult = null; - - if (!string.IsNullOrWhiteSpace(ComputerSamAccountName)) - ComputerSamAccountName = ComputerSamAccountName.TrimEnd('$'); - if (string.IsNullOrWhiteSpace(ComputerSamAccountName) || ComputerSamAccountName.Length > 24) - throw new System.ArgumentException("Invalid Computer Name; > 0 and <= 24", "ComputerName"); - - // Ensure Specified OU Exists - if (!string.IsNullOrEmpty(OrganisationalUnit)) - { - try - { - using (var deOU = DomainController.RetrieveDirectoryEntry(OrganisationalUnit)) - { - if (deOU == null) - throw new Exception(string.Format("OU's Directory Entry couldn't be found at [{0}]", OrganisationalUnit)); - } - } - catch (Exception ex) - { - throw new ArgumentException(string.Format("An error occurred while trying to locate the specified OU: {0}", OrganisationalUnit), "OrganisationalUnit", ex); - } - } - - string tempFileName = System.IO.Path.GetTempFileName(); - string argumentOU = (!string.IsNullOrWhiteSpace(OrganisationalUnit)) ? string.Format(" /MACHINEOU \"{0}\"", OrganisationalUnit) : string.Empty; - string arguments = string.Format("/PROVISION /DOMAIN \"{0}\" /DCNAME \"{1}\" /MACHINE \"{2}\"{3} /REUSE /SAVEFILE \"{4}\"", - Domain.DnsName, - DomainController.Name, - ComputerSamAccountName, - argumentOU, - tempFileName - ); - ProcessStartInfo commandStarter = new ProcessStartInfo("DJOIN.EXE", arguments) - { - CreateNoWindow = true, - ErrorDialog = false, - LoadUserProfile = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false - }; - diagnosticInfo.AppendFormat("{0} {1}", "DJOIN.EXE", arguments); - diagnosticInfo.AppendLine(); - - string stdOutput; - string stdError; - using (Process commandProc = Process.Start(commandStarter)) - { - commandProc.WaitForExit(20000); - stdOutput = commandProc.StandardOutput.ReadToEnd(); - stdError = commandProc.StandardError.ReadToEnd(); - } - if (!string.IsNullOrWhiteSpace(stdOutput)) - diagnosticInfo.AppendLine(stdOutput); - if (!string.IsNullOrWhiteSpace(stdError)) - diagnosticInfo.AppendLine(stdError); - - if (System.IO.File.Exists(tempFileName)) - { - DJoinResult = System.Convert.ToBase64String(System.IO.File.ReadAllBytes(tempFileName)); - System.IO.File.Delete(tempFileName); - } - if (string.IsNullOrWhiteSpace(DJoinResult)) - throw new System.InvalidOperationException(string.Format("Domain Join Unsuccessful{0}Error: {1}{0}Output: {2}", System.Environment.NewLine, stdError, stdOutput)); - - DiagnosticInformation = diagnosticInfo.ToString(); - - return DJoinResult; - } - - public static DirectoryEntry RetrieveDirectoryEntry(string DistinguishedName, out ActiveDirectoryDomain Domain) - { - if (string.IsNullOrWhiteSpace(DistinguishedName)) - throw new ArgumentNullException("DistinguishedName"); - - // Find Domain - var domain = GetDomainByDistinguishedName(DistinguishedName); - - if (domain == null) - throw new ArgumentException(string.Format("Unknown domain for DistinguishedName: {0}", DistinguishedName), "DistinguishedName"); - - var domainDC = Site.RetrieveReachableDomainControllers(domain).FirstOrDefault(); - var ldapServer = domainDC != null ? domainDC.Name : domain.DnsName; - - Domain = domain; - - return new DirectoryEntry(string.Format(@"LDAP://{0}/{1}", ldapServer, DistinguishedName)); - } - - public static DomainController RetrieveWritableDomainController(this ActiveDirectoryDomain domain) - { - var adContext = new DirectoryContext(DirectoryContextType.Domain, domain.DnsName); - using (var adDomain = Domain.GetDomain(adContext)) - { - return adDomain.FindDomainController(LocatorOptions.WriteableRequired); - } - } - - public static DirectoryEntry RetrieveDirectoryEntry(this DomainController domainController, string DistinguishedName) - { - return new DirectoryEntry(string.Format(@"LDAP://{0}/{1}", domainController.Name, DistinguishedName)); - } - - public static void DeleteObjectRecursive(this DirectoryEntry directoryEntry) - { - DeleteObjectRecursiveInternal(directoryEntry); - - using (var deParent = directoryEntry.Parent) - { - deParent.Children.Remove(directoryEntry); - } - } - private static void DeleteObjectRecursiveInternal(DirectoryEntry directoryEntry) - { - List children = directoryEntry.Children.Cast().ToList(); - - foreach (var child in children) - { - DeleteObjectRecursive(child); - directoryEntry.Children.Remove(child); - child.Dispose(); - } - } - - #region Helpers - - internal static string ConvertBytesToSDDLString(byte[] SID) - { - SecurityIdentifier sID = new SecurityIdentifier(SID, 0); - - return sID.ToString(); - } - - internal static byte[] ConvertSDDLStringToBytes(string SidSsdlString) - { - SecurityIdentifier sID = new SecurityIdentifier(SidSsdlString); - - var sidBytes = new byte[sID.BinaryLength]; - - sID.GetBinaryForm(sidBytes, 0); - - return sidBytes; - } - - internal static byte[] BuildPrimaryGroupSid(byte[] UserSID, int PrimaryGroupId) - { - var groupSid = (byte[])UserSID.Clone(); - - int ridOffset = groupSid.Length - 4; - int groupId = PrimaryGroupId; - for (int i = 0; i < 4; i++) - { - groupSid[ridOffset + i] = (byte)(groupId & 0xFF); - groupId >>= 8; - } - - return groupSid; - } - - internal static string ConvertBytesToBinarySidString(byte[] SID) - { - StringBuilder escapedSid = new StringBuilder(); - - foreach (var sidByte in SID) - { - escapedSid.Append('\\'); - escapedSid.Append(sidByte.ToString("x2")); - } - - return escapedSid.ToString(); - } - - internal static string EscapeLdapQuery(string query) - { - return query.Replace("*", "\\2a").Replace("(", "\\28").Replace(")", "\\29").Replace("\\", "\\5c").Replace("NUL", "\\00").Replace("/", "\\2f"); - } - internal static string FormatGuidForLdapQuery(System.Guid g) - { - checked - { - System.Text.StringBuilder sb = new System.Text.StringBuilder(); - byte[] array = g.ToByteArray(); - for (int i = 0; i < array.Length; i++) - { - byte b = array[i]; - sb.Append("\\"); - sb.Append(b.ToString("X2")); - } - return sb.ToString(); - } - } - - #endregion - } -} diff --git a/Disco.Services/Interop/ActiveDirectory/Internal/ADUpdateLastNetworkLogonDateJob.cs b/Disco.Services/Interop/ActiveDirectory/Internal/ADUpdateLastNetworkLogonDateJob.cs deleted file mode 100644 index 3703653a..00000000 --- a/Disco.Services/Interop/ActiveDirectory/Internal/ADUpdateLastNetworkLogonDateJob.cs +++ /dev/null @@ -1,231 +0,0 @@ -using Disco.Data.Repository; -using Disco.Models.Interop.ActiveDirectory; -using Disco.Models.Repository; -using Disco.Services.Logging; -using Disco.Services.Tasks; -using Quartz; -using System; -using System.Collections; -using System.Collections.Generic; -using System.DirectoryServices; -using System.DirectoryServices.ActiveDirectory; -using System.Linq; - -namespace Disco.Services.Interop.ActiveDirectory.Internal -{ - public class ADUpdateLastNetworkLogonDateJob : ScheduledTask - { - - public override string TaskName { get { return "Active Directory - Update Last Network Logon Dates Task"; } } - public override bool SingleInstanceTask { get { return true; } } - public override bool CancelInitiallySupported { get { return false; } } - - public override void InitalizeScheduledTask(DiscoDataContext Database) - { - // ActiveDirectoryUpdateLastNetworkLogonDateJob @ 11:30pm - TriggerBuilder triggerBuilder = TriggerBuilder.Create(). - WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(23, 30)); - - this.ScheduleTask(triggerBuilder); - } - - protected override void ExecuteTask() - { - int changeCount; - - this.Status.UpdateStatus(1, "Starting", "Connecting to the Database and initializing the environment"); - using (DiscoDataContext database = new DiscoDataContext()) - { - UpdateLastNetworkLogonDates(database, this.Status); - this.Status.UpdateStatus(95, "Updating Database", "Writing last network logon dates to the Database"); - changeCount = database.SaveChanges(); - this.Status.Finished(string.Format("{0} Device last network logon dates updated", changeCount), "/Config/SystemConfig"); - } - - SystemLog.LogInformation(new string[] - { - "Updated LastNetworkLogon Device Property for Device/s", - changeCount.ToString() - }); - } - - public static ScheduledTaskStatus ScheduleImmediately() - { - var existingTask = ScheduledTasks.GetTaskStatuses(typeof(ADUpdateLastNetworkLogonDateJob)).Where(s => s.IsRunning).FirstOrDefault(); - if (existingTask != null) - return existingTask; - - var instance = new ADUpdateLastNetworkLogonDateJob(); - return instance.ScheduleTask(); - } - - public static bool UpdateLastNetworkLogonDate(Device Device) - { - const string ldapFilterTemplate = "(&(objectCategory=Computer)(sAMAccountName={0}))"; - string[] ldapProperties = new string[] { "lastLogon", "lastLogonTimestamp" }; - - System.DateTime? lastLogon = null; - - if (!string.IsNullOrEmpty(Device.DeviceDomainId)) - { - var deviceSamAccountName = UserExtensions.SplitUserId(Device.DeviceDomainId).Item2 + "$"; - var ldapFilter = string.Format(ldapFilterTemplate, ADInterop.EscapeLdapQuery(deviceSamAccountName)); - - var domain = ADInterop.GetDomainFromId(Device.DeviceDomainId); - IEnumerable domainControllers; - - if (ADInterop.SearchEntireForest) - domainControllers = domain.RetrieveReachableDomainControllers(); - else - domainControllers = ADInterop.Site.RetrieveReachableDomainControllers(domain); - - lastLogon = domainControllers.Select(dc => - { - using (var directoryRoot = dc.RetrieveDirectoryEntry(domain.DistinguishedName)) - { - using (var directorySearcher = new DirectorySearcher(directoryRoot, ldapFilter, ldapProperties, SearchScope.Subtree)) - { - var directoryResult = directorySearcher.FindOne(); - - if (directoryResult != null) - { - long lastLogonValue = default(long); - long lastLogonTimestampValue = default(long); - - var lastLogonProperty = directoryResult.Properties["lastLogon"]; - if (lastLogonProperty != null && lastLogonProperty.Count > 0) - lastLogonValue = (long)lastLogonProperty[0]; - var lastLogonTimestampProperty = directoryResult.Properties["lastLogonTimestamp"]; - if (lastLogonTimestampProperty != null && lastLogonTimestampProperty.Count > 0) - lastLogonTimestampValue = (long)lastLogonTimestampProperty[0]; - - long highedValue = Math.Max(lastLogonValue, lastLogonTimestampValue); - - if (highedValue > 0) - return (DateTime?)new DateTime((DateTime.FromFileTime(highedValue).Ticks / 10000000L) * 10000000L); - else - return null; - } - } - } - return null; - }).Where(dt => dt.HasValue).Max(); - } - - if (lastLogon.HasValue && - ( - !Device.LastNetworkLogonDate.HasValue - || Device.LastNetworkLogonDate.Value < lastLogon - )) - { - Device.LastNetworkLogonDate = lastLogon; - return true; - } - return false; - } - - private static void UpdateLastNetworkLogonDates(DiscoDataContext Database, ScheduledTaskStatus status) - { - const string ldapFilter = "(objectCategory=Computer)"; - string[] ldapProperties = new string[] { "sAMAccountName", "lastLogon" }; - - status.UpdateStatus(2, "Initializing", "Determining Domains and Available Domain Controllers"); - - // Determine Domain Controllers to Query - IEnumerable> domainControllers; - if (ADInterop.SearchEntireForest) - domainControllers = ADInterop.Domains.SelectMany(d => d.RetrieveReachableDomainControllers(), (d, dc) => Tuple.Create(d, dc)); - else - domainControllers = ADInterop.Domains.SelectMany(d => ADInterop.Site.RetrieveReachableDomainControllers(d), (d, dc) => Tuple.Create(d, dc)); - - // Determine Queries - var requiredRueries = domainControllers - .Where(s => s.Item1.SearchContainers != null && s.Item1.SearchContainers.Count > 0) - .SelectMany(s => s.Item1.SearchContainers, (s, c) => Tuple.Create(s.Item1, s.Item2, c)).ToList(); - - var queries = Enumerable.Range(0, requiredRueries.Count).Select(i => - { - var q = requiredRueries[i]; - return Tuple.Create(i, q.Item1, q.Item2, q.Item3); - }); - - var queryResults = queries.SelectMany(q => - { - var queryIndex = q.Item1; - var domain = q.Item2; - var domainController = q.Item3; - var searchRoot = q.Item4; - - // Update Status - double progress = 5 + (queryIndex * (90 / requiredRueries.Count)); - status.UpdateStatus(progress, string.Format("Querying Domain [{0}] using controller [{1}]", domain.NetBiosName, domainController.Name), string.Format("Searching: {0}", searchRoot)); - - // Perform Query - using (var directoryRoot = domainController.RetrieveDirectoryEntry(searchRoot)) - { - using (var directorySearcher = new DirectorySearcher(directoryRoot, ldapFilter, ldapProperties, SearchScope.Subtree)) - { - directorySearcher.PageSize = 500; - - var directoryResults = directorySearcher.FindAll(); - - if (directoryResults != null) - { - return directoryResults.Cast().Select(result => - { - var samAccountProperity = result.Properties["sAMAccountName"]; - - - long lastLogonValue = default(long); - long lastLogonTimestampValue = default(long); - - var lastLogonProperty = result.Properties["lastLogon"]; - if (lastLogonProperty != null && lastLogonProperty.Count > 0) - lastLogonValue = (long)lastLogonProperty[0]; - var lastLogonTimestampProperty = result.Properties["lastLogonTimestamp"]; - if (lastLogonTimestampProperty != null && lastLogonTimestampProperty.Count > 0) - lastLogonTimestampValue = (long)lastLogonTimestampProperty[0]; - - long highedValue = Math.Max(lastLogonValue, lastLogonTimestampValue); - - if (highedValue > 0) - { - var computerName = string.Format(@"{0}\{1}", domain.NetBiosName, samAccountProperity[0].ToString().TrimEnd('$')); - var lastLogon = new DateTime((DateTime.FromFileTime(highedValue).Ticks / 10000000L) * 10000000L); - return Tuple.Create(computerName, lastLogon); - } - else - return null; - }).Where(i => i != null).ToList(); - } - else - { - return Enumerable.Empty>(); - } - } - } - }).GroupBy(r => r.Item1, StringComparer.InvariantCultureIgnoreCase).ToDictionary(g => g.Key.ToUpper(), g => g.Max(i => i.Item2)); - - status.UpdateStatus(90, "Processing Results", "Processing last network logon dates and looking for updates"); - - foreach (Device device in Database.Devices.Where(device => device.DeviceDomainId != null)) - { - DateTime lastLogonDate; - if (queryResults.TryGetValue(device.DeviceDomainId.ToUpper(), out lastLogonDate)) - { - if (!device.LastNetworkLogonDate.HasValue) - device.LastNetworkLogonDate = lastLogonDate; - else - { - // Change accuracy to the second - lastLogonDate = new DateTime((lastLogonDate.Ticks / 10000000L) * 10000000L); - - if (device.LastNetworkLogonDate.Value < lastLogonDate) - device.LastNetworkLogonDate = lastLogonDate; - } - } - } - } - - } -} diff --git a/Disco.Services/Jobs/JobLists/JobTableExtensions.cs b/Disco.Services/Jobs/JobLists/JobTableExtensions.cs index 7634ab50..57a16d58 100644 --- a/Disco.Services/Jobs/JobLists/JobTableExtensions.cs +++ b/Disco.Services/Jobs/JobLists/JobTableExtensions.cs @@ -274,12 +274,12 @@ namespace Disco.Services Location = i, References = o.ToList() }, - StringComparer.InvariantCultureIgnoreCase); + StringComparer.OrdinalIgnoreCase); } public static IEnumerable JobLocationReferences(this IEnumerable Items) { return Items.Where(i => !string.IsNullOrWhiteSpace(i.DeviceHeldLocation) && i.DeviceHeld.HasValue && !i.DeviceReturnedDate.HasValue) - .GroupBy(i => i.DeviceHeldLocation, StringComparer.InvariantCultureIgnoreCase) + .GroupBy(i => i.DeviceHeldLocation, StringComparer.OrdinalIgnoreCase) .Select(i => new JobLocationReference() { Location = i.Key, diff --git a/Disco.Services/Logging/LogContext.cs b/Disco.Services/Logging/LogContext.cs index 69d91577..4be5a8f1 100644 --- a/Disco.Services/Logging/LogContext.cs +++ b/Disco.Services/Logging/LogContext.cs @@ -56,7 +56,7 @@ namespace Disco.Services.Logging var appDomain = AppDomain.CurrentDomain; var logModuleTypes = (from a in appDomain.GetAssemblies() - where !a.GlobalAssemblyCache && !a.IsDynamic && a.FullName.StartsWith("Disco.", StringComparison.InvariantCultureIgnoreCase) + where !a.GlobalAssemblyCache && !a.IsDynamic && a.FullName.StartsWith("Disco.", StringComparison.OrdinalIgnoreCase) from type in a.GetTypes() where typeof(LogBase).IsAssignableFrom(type) && !type.IsAbstract select type); diff --git a/Disco.Services/Plugins/Features/WarrantyProvider/WarrantyProviderFeature.cs b/Disco.Services/Plugins/Features/WarrantyProvider/WarrantyProviderFeature.cs index ec670b43..b6077d51 100644 --- a/Disco.Services/Plugins/Features/WarrantyProvider/WarrantyProviderFeature.cs +++ b/Disco.Services/Plugins/Features/WarrantyProvider/WarrantyProviderFeature.cs @@ -28,7 +28,7 @@ namespace Disco.Services.Plugins.Features.WarrantyProvider public static PluginFeatureManifest FindPluginFeature(string PluginIdOrWarrantyProviderId) { var defs = Plugins.GetPluginFeatures(typeof(WarrantyProviderFeature)); - var def = defs.FirstOrDefault(d => d.PluginManifest.Id.Equals(PluginIdOrWarrantyProviderId, StringComparison.InvariantCultureIgnoreCase)); + var def = defs.FirstOrDefault(d => d.PluginManifest.Id.Equals(PluginIdOrWarrantyProviderId, StringComparison.OrdinalIgnoreCase)); if (def != null) return def; else @@ -36,7 +36,7 @@ namespace Disco.Services.Plugins.Features.WarrantyProvider { using (var providerInstance = d.CreateInstance()) { - if (providerInstance.WarrantyProviderId != null && providerInstance.WarrantyProviderId.Equals(PluginIdOrWarrantyProviderId, StringComparison.InvariantCultureIgnoreCase)) + if (providerInstance.WarrantyProviderId != null && providerInstance.WarrantyProviderId.Equals(PluginIdOrWarrantyProviderId, StringComparison.OrdinalIgnoreCase)) { return d; } diff --git a/Disco.Services/Plugins/InstallPluginTask.cs b/Disco.Services/Plugins/InstallPluginTask.cs index afe36b8d..7e483ee1 100644 --- a/Disco.Services/Plugins/InstallPluginTask.cs +++ b/Disco.Services/Plugins/InstallPluginTask.cs @@ -101,7 +101,7 @@ namespace Disco.Services.Plugins // Check for Compatibility var compatibilityData = Plugins.LoadCompatibilityData(database); - var pluginCompatibility = compatibilityData.Plugins.FirstOrDefault(i => i.Id.Equals(packageManifest.Id, StringComparison.InvariantCultureIgnoreCase) && packageManifest.Version == Version.Parse(i.Version)); + var pluginCompatibility = compatibilityData.Plugins.FirstOrDefault(i => i.Id.Equals(packageManifest.Id, StringComparison.OrdinalIgnoreCase) && packageManifest.Version == Version.Parse(i.Version)); if (pluginCompatibility != null && !pluginCompatibility.Compatible) throw new InvalidOperationException(string.Format("The plugin [{0} v{1}] is not compatible: {2}", packageManifest.Id, packageManifest.VersionFormatted, pluginCompatibility.Reason)); diff --git a/Disco.Services/Plugins/PluginManifest.cs b/Disco.Services/Plugins/PluginManifest.cs index 830798d6..7bca31c8 100644 --- a/Disco.Services/Plugins/PluginManifest.cs +++ b/Disco.Services/Plugins/PluginManifest.cs @@ -240,7 +240,7 @@ namespace Disco.Services.Plugins foreach (string referenceFilename in Directory.EnumerateFiles(pluginLocation, "*.dll", SearchOption.TopDirectoryOnly)) { - if (!referenceFilename.Equals(assembly.Location, StringComparison.InvariantCultureIgnoreCase)) + if (!referenceFilename.Equals(assembly.Location, StringComparison.OrdinalIgnoreCase)) { // Ignore Excluded Assemblies if (!PluginExcludedAssemblies.Contains(Path.GetFileNameWithoutExtension(referenceFilename))) diff --git a/Disco.Services/Plugins/Plugins.cs b/Disco.Services/Plugins/Plugins.cs index b1e1d3be..daec98b7 100644 --- a/Disco.Services/Plugins/Plugins.cs +++ b/Disco.Services/Plugins/Plugins.cs @@ -302,7 +302,7 @@ namespace Disco.Services.Plugins foreach (var serverItem in serverData.Plugins) { var serverItemVersion = Version.Parse(serverItem.Version); - var localItem = localItems.FirstOrDefault(i => i.Id.Equals(serverItem.Id, StringComparison.InvariantCultureIgnoreCase) && serverItemVersion == localItemVersions[i]); + var localItem = localItems.FirstOrDefault(i => i.Id.Equals(serverItem.Id, StringComparison.OrdinalIgnoreCase) && serverItemVersion == localItemVersions[i]); if (localItem != null) joinedItems.Remove(localItem); @@ -373,7 +373,7 @@ namespace Disco.Services.Plugins if (pluginManifest != null) { // Check Version Compatibility - var pluginCompatibility = compatibilityData.Value.Plugins.FirstOrDefault(i => i.Id.Equals(pluginManifest.Id, StringComparison.InvariantCultureIgnoreCase) && pluginManifest.Version == Version.Parse(i.Version)); + var pluginCompatibility = compatibilityData.Value.Plugins.FirstOrDefault(i => i.Id.Equals(pluginManifest.Id, StringComparison.OrdinalIgnoreCase) && pluginManifest.Version == Version.Parse(i.Version)); if (pluginCompatibility != null && !pluginCompatibility.Compatible) throw new InvalidOperationException(string.Format("The plugin [{0} v{1}] is not compatible: {2}", pluginManifest.Id, pluginManifest.VersionFormatted, pluginCompatibility.Reason)); @@ -496,7 +496,7 @@ namespace Disco.Services.Plugins // Check Compatibility if (CompatibilityData == null) CompatibilityData = LoadCompatibilityData(Database); - var pluginCompatibility = CompatibilityData.Plugins.FirstOrDefault(i => i.Id.Equals(packageManifest.Id, StringComparison.InvariantCultureIgnoreCase) && packageManifest.Version == Version.Parse(i.Version)); + var pluginCompatibility = CompatibilityData.Plugins.FirstOrDefault(i => i.Id.Equals(packageManifest.Id, StringComparison.OrdinalIgnoreCase) && packageManifest.Version == Version.Parse(i.Version)); if (pluginCompatibility != null && !pluginCompatibility.Compatible) throw new InvalidOperationException(string.Format("The plugin [{0} v{1}] is not compatible: {2}", packageManifest.Id, packageManifest.VersionFormatted, pluginCompatibility.Reason)); @@ -595,7 +595,7 @@ namespace Disco.Services.Plugins public static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { - if (args.RequestingAssembly != null && args.RequestingAssembly.Location.StartsWith(PluginPath, StringComparison.InvariantCultureIgnoreCase) && _PluginManifests != null) + if (args.RequestingAssembly != null && args.RequestingAssembly.Location.StartsWith(PluginPath, StringComparison.OrdinalIgnoreCase) && _PluginManifests != null) { // Try best guess first PluginManifest requestingPlugin = _PluginManifests.Values.Where(p => p.Type.Assembly == args.RequestingAssembly).FirstOrDefault(); diff --git a/Disco.Services/Plugins/UpdatePluginTask.cs b/Disco.Services/Plugins/UpdatePluginTask.cs index 6ecc1f61..3fffc683 100644 --- a/Disco.Services/Plugins/UpdatePluginTask.cs +++ b/Disco.Services/Plugins/UpdatePluginTask.cs @@ -235,7 +235,7 @@ namespace Disco.Services.Plugins { // Check for Compatibility var compatibilityData = Plugins.LoadCompatibilityData(database); - var pluginCompatibility = compatibilityData.Plugins.FirstOrDefault(i => i.Id.Equals(updateManifest.Id, StringComparison.InvariantCultureIgnoreCase) && updateManifest.Version == Version.Parse(i.Version)); + var pluginCompatibility = compatibilityData.Plugins.FirstOrDefault(i => i.Id.Equals(updateManifest.Id, StringComparison.OrdinalIgnoreCase) && updateManifest.Version == Version.Parse(i.Version)); if (pluginCompatibility != null && !pluginCompatibility.Compatible) throw new InvalidOperationException(string.Format("The plugin [{0} v{1}] is not compatible: {2}", updateManifest.Id, updateManifest.VersionFormatted, pluginCompatibility.Reason)); diff --git a/Disco.Services/Searching/Search.cs b/Disco.Services/Searching/Search.cs index 8c286597..7b54bf86 100644 --- a/Disco.Services/Searching/Search.cs +++ b/Disco.Services/Searching/Search.cs @@ -1,5 +1,4 @@ using Disco.Data.Repository; -using Disco.Models.Interop.ActiveDirectory; using Disco.Models.Repository; using Disco.Models.Services.Jobs.JobLists; using Disco.Models.Services.Searching; @@ -171,7 +170,7 @@ namespace Disco.Services.Searching public static List SearchUsersUpstream(string Term, int? LimitCount = null) { - IEnumerable matches = ActiveDirectory.SearchUserAccounts(Term, Quick: true); + IEnumerable matches = ActiveDirectory.SearchADUserAccounts(Term, Quick: true); if (LimitCount.HasValue) matches = matches.Take(LimitCount.Value); diff --git a/Disco.Services/Users/UserExtensions.cs b/Disco.Services/Users/UserExtensions.cs index f7bdc0ed..f4c888eb 100644 --- a/Disco.Services/Users/UserExtensions.cs +++ b/Disco.Services/Users/UserExtensions.cs @@ -12,7 +12,7 @@ namespace Disco.Services { public static bool IsInPrimaryDomain(this User u) { - return u.Domain.Equals(Disco.Services.Interop.ActiveDirectory.ActiveDirectory.PrimaryDomain.NetBiosName, StringComparison.InvariantCultureIgnoreCase); + return u.Domain.Equals(ActiveDirectory.Context.PrimaryDomain.NetBiosName, StringComparison.OrdinalIgnoreCase); } public static string FriendlyId(this User u) @@ -23,8 +23,8 @@ namespace Disco.Services public static string FriendlyUserId(string UserId) { var splitUserId = SplitUserId(UserId); - - if (splitUserId.Item1 != null && splitUserId.Item1.Equals(ActiveDirectory.PrimaryDomain.NetBiosName, StringComparison.InvariantCultureIgnoreCase)) + + if (splitUserId.Item1 != null && splitUserId.Item1.Equals(ActiveDirectory.Context.PrimaryDomain.NetBiosName, StringComparison.OrdinalIgnoreCase)) return splitUserId.Item2; else return UserId; diff --git a/Disco.Services/Users/UserService.cs b/Disco.Services/Users/UserService.cs index 87b7dea8..aa43b4c6 100644 --- a/Disco.Services/Users/UserService.cs +++ b/Disco.Services/Users/UserService.cs @@ -1,5 +1,4 @@ using Disco.Data.Repository; -using Disco.Models.Interop.ActiveDirectory; using Disco.Models.Repository; using Disco.Services.Authorization; using Disco.Services.Authorization.Roles; @@ -206,9 +205,9 @@ namespace Disco.Services.Users Cache.FlushCache(); } - internal static IEnumerable SearchUsers(DiscoDataContext Database, string Term) + internal static IEnumerable SearchUsers(DiscoDataContext Database, string Term) { - var adImportedUsers = ActiveDirectory.SearchUserAccounts(Term, Quick: true); + var adImportedUsers = ActiveDirectory.SearchADUserAccounts(Term, Quick: true); foreach (var adU in adImportedUsers.Select(adU => adU.ToRepositoryUser())) { var existingUser = Database.Users.Find(adU.UserId); @@ -230,7 +229,7 @@ namespace Disco.Services.Users if (UserId.EndsWith("$")) { // Machine Account - var adAccount = ActiveDirectory.RetrieveMachineAccount(UserId); + var adAccount = ActiveDirectory.RetrieveADMachineAccount(UserId); if (adAccount == null) return null; @@ -244,10 +243,10 @@ namespace Disco.Services.Users { // User Account - ActiveDirectoryUserAccount adAccount; + ADUserAccount adAccount; try { - adAccount = ActiveDirectory.RetrieveUserAccount(UserId); + adAccount = ActiveDirectory.RetrieveADUserAccount(UserId); if (adAccount == null) throw new ArgumentException(string.Format("Invalid Username: '{0}'; User not found in Active Directory", UserId), "Username"); @@ -280,7 +279,7 @@ namespace Disco.Services.Users } Database.SaveChanges(); - var token = AuthorizationToken.BuildToken(user, adAccount.Groups); + var token = AuthorizationToken.BuildToken(user, adAccount.Groups.Select(g => g.Id)); return new Tuple(user, token); } diff --git a/Disco.Web/Areas/API/Controllers/AuthorizationRoleController.cs b/Disco.Web/Areas/API/Controllers/AuthorizationRoleController.cs index 0e02e32b..816cf549 100644 --- a/Disco.Web/Areas/API/Controllers/AuthorizationRoleController.cs +++ b/Disco.Web/Areas/API/Controllers/AuthorizationRoleController.cs @@ -1,5 +1,4 @@ using Disco.BI.Extensions; -using Disco.Models.Interop.ActiveDirectory; using Disco.Models.Repository; using Disco.Services.Authorization; using Disco.Services.Authorization.Roles; @@ -16,7 +15,6 @@ namespace Disco.Web.Areas.API.Controllers [DiscoAuthorize(Claims.DiscoAdminAccount)] public partial class AuthorizationRoleController : AuthorizedDatabaseController { - #region Properties const string pName = "name"; @@ -111,14 +109,15 @@ namespace Disco.Web.Areas.API.Controllers var subjects = Subjects .Where(s => !string.IsNullOrWhiteSpace(s)) .Select(s => s.Trim()) - .Select(s => Tuple.Create(s, ActiveDirectory.RetrieveObject(s, Quick: true))) + .Select(s => Tuple.Create(s, ActiveDirectory.RetrieveADObject(s, Quick: true))) + .Where(s => s.Item2 is ADUserAccount || s.Item2 is ADGroup) .ToList(); var invalidSubjects = subjects.Where(s => s.Item2 == null).ToList(); if (invalidSubjects.Count > 0) throw new ArgumentException(string.Format("Subjects not found: {0}", string.Join(", ", invalidSubjects)), "Subjects"); - var proposedSubjects = subjects.Select(s => s.Item2.NetBiosId).OrderBy(s => s).ToArray(); + var proposedSubjects = subjects.Select(s => s.Item2.Id).OrderBy(s => s).ToArray(); var currentSubjects = AuthorizationRole.SubjectIds == null ? new string[0] : AuthorizationRole.SubjectIds.Split(','); removedSubjects = currentSubjects.Except(proposedSubjects).ToArray(); addedSubjects = proposedSubjects.Except(currentSubjects).ToArray(); @@ -249,14 +248,15 @@ namespace Disco.Web.Areas.API.Controllers var subjects = Subjects .Where(s => !string.IsNullOrWhiteSpace(s)) .Select(s => s.Trim()) - .Select(s => Tuple.Create(s, ActiveDirectory.RetrieveObject(s, Quick: true))) + .Select(s => Tuple.Create(s, ActiveDirectory.RetrieveADObject(s, Quick: true))) + .Where(s => s.Item2 is ADUserAccount || s.Item2 is ADGroup) .ToList(); var invalidSubjects = subjects.Where(s => s.Item2 == null).ToList(); if (invalidSubjects.Count > 0) throw new ArgumentException(string.Format("Subjects not found: {0}", string.Join(", ", invalidSubjects)), "Subjects"); - proposedSubjects = subjects.Select(s => s.Item2.NetBiosId).OrderBy(s => s).ToArray(); + proposedSubjects = subjects.Select(s => s.Item2.Id).OrderBy(s => s).ToArray(); var currentSubjects = UserService.AdministratorSubjectIds; removedSubjects = currentSubjects.Except(proposedSubjects).ToArray(); addedSubjects = proposedSubjects.Except(currentSubjects).ToArray(); @@ -275,31 +275,5 @@ namespace Disco.Web.Areas.API.Controllers } #endregion - - public virtual ActionResult SearchSubjects(string term) - { - var groupResults = ActiveDirectory.SearchGroups(term).Cast(); - var userResults = ActiveDirectory.SearchUserAccounts(term).Cast(); - - var results = groupResults.Concat(userResults).OrderBy(r => r.SamAccountName) - .Select(r => Models.AuthorizationRole.SubjectItem.FromActiveDirectoryObject(r)).ToList(); - - return Json(results, JsonRequestBehavior.AllowGet); - } - - public virtual ActionResult Subject(string Id) - { - if (string.IsNullOrWhiteSpace(Id)) - return Json(null, JsonRequestBehavior.AllowGet); - else if (!Id.Contains(@"\")) - Id = string.Format(@"{0}\{1}", ActiveDirectory.PrimaryDomain.NetBiosName, Id); - - var subject = ActiveDirectory.RetrieveObject(Id, Quick: true); - - if (subject == null || !(subject is ActiveDirectoryUserAccount || subject is ActiveDirectoryGroup)) - return Json(null, JsonRequestBehavior.AllowGet); - else - return Json(Models.AuthorizationRole.SubjectItem.FromActiveDirectoryObject(subject), JsonRequestBehavior.AllowGet); - } } } diff --git a/Disco.Web/Areas/API/Controllers/DeviceController.cs b/Disco.Web/Areas/API/Controllers/DeviceController.cs index 22bfc633..db6d8330 100644 --- a/Disco.Web/Areas/API/Controllers/DeviceController.cs +++ b/Disco.Web/Areas/API/Controllers/DeviceController.cs @@ -150,7 +150,7 @@ namespace Disco.Web.Areas.API.Controllers // Update AD Account if (!string.IsNullOrEmpty(device.DeviceDomainId) && device.DeviceDomainId.Length <= 24) { - var adMachineAccount = ActiveDirectory.RetrieveMachineAccount(device.DeviceDomainId); + var adMachineAccount = ActiveDirectory.RetrieveADMachineAccount(device.DeviceDomainId); if (adMachineAccount != null) adMachineAccount.SetDescription(device); } @@ -410,7 +410,7 @@ namespace Disco.Web.Areas.API.Controllers var thumbPath = da.RepositoryThumbnailFilename(Database); if (System.IO.File.Exists(thumbPath)) { - if (thumbPath.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase)) + if (thumbPath.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) return File(thumbPath, "image/png"); else return File(thumbPath, "image/jpg"); @@ -433,7 +433,7 @@ namespace Disco.Web.Areas.API.Controllers if (file.ContentLength > 0) { var contentType = file.ContentType; - if (string.IsNullOrEmpty(contentType) || contentType.Equals("unknown/unknown", StringComparison.InvariantCultureIgnoreCase)) + if (string.IsNullOrEmpty(contentType) || contentType.Equals("unknown/unknown", StringComparison.OrdinalIgnoreCase)) contentType = BI.Interop.MimeTypes.ResolveMimeType(file.FileName); var da = new Disco.Models.Repository.DeviceAttachment() @@ -501,7 +501,7 @@ namespace Disco.Web.Areas.API.Controllers var da = Database.DeviceAttachments.Include("TechUser").Where(m => m.Id == id).FirstOrDefault(); if (da != null) { - if (da.TechUserId.Equals(CurrentUser.UserId, StringComparison.InvariantCultureIgnoreCase)) + if (da.TechUserId.Equals(CurrentUser.UserId, StringComparison.OrdinalIgnoreCase)) Authorization.RequireAny(Claims.Device.Actions.RemoveAnyAttachments, Claims.Device.Actions.RemoveOwnAttachments); else Authorization.Require(Claims.Device.Actions.RemoveAnyAttachments); diff --git a/Disco.Web/Areas/API/Controllers/DeviceProfileController.cs b/Disco.Web/Areas/API/Controllers/DeviceProfileController.cs index e73eab4b..b5d5fd61 100644 --- a/Disco.Web/Areas/API/Controllers/DeviceProfileController.cs +++ b/Disco.Web/Areas/API/Controllers/DeviceProfileController.cs @@ -246,7 +246,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateOrganisationalUnit(Disco.Models.Repository.DeviceProfile deviceProfile, string OrganisationalUnit) { if (string.IsNullOrWhiteSpace(OrganisationalUnit)) - OrganisationalUnit = ActiveDirectory.PrimaryDomain.GetDefaultComputerContainer(); + OrganisationalUnit = ActiveDirectory.Context.PrimaryDomain.DefaultComputerContainer; if (OrganisationalUnit != deviceProfile.OrganisationalUnit) { diff --git a/Disco.Web/Areas/API/Controllers/JobController.cs b/Disco.Web/Areas/API/Controllers/JobController.cs index 3d814914..6c1da7a3 100644 --- a/Disco.Web/Areas/API/Controllers/JobController.cs +++ b/Disco.Web/Areas/API/Controllers/JobController.cs @@ -531,7 +531,7 @@ namespace Disco.Web.Areas.API.Controllers // Enforce Restricted List Mode var value = DeviceHeldLocation.Trim(); - if (!Database.DiscoConfiguration.JobPreferences.LocationList.Contains(value, StringComparer.InvariantCultureIgnoreCase)) + if (!Database.DiscoConfiguration.JobPreferences.LocationList.Contains(value, StringComparer.OrdinalIgnoreCase)) throw new ArgumentException("The location was not found in the list (Mode: Restricted List)"); } @@ -545,7 +545,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateFlags(Job job, string Flags) { // Only User Management Job Supports Flags at the moment - if (!job.JobTypeId.Equals(JobType.JobTypeIds.UMgmt, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.UMgmt, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for User Management Jobs"); } @@ -602,7 +602,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateNonWarrantyIsInsuranceClaim(Job job, string IsInsuranceClaim) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -641,7 +641,7 @@ namespace Disco.Web.Areas.API.Controllers private Models.Job._DateChangeModel UpdateNonWarrantyAccountingChargeRequired(Job job, string AccountingChargeRequiredDate) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -675,7 +675,7 @@ namespace Disco.Web.Areas.API.Controllers private Models.Job._DateChangeModel UpdateNonWarrantyAccountingChargeAdded(Job job, string AccountingChargeAddedDate) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -709,7 +709,7 @@ namespace Disco.Web.Areas.API.Controllers private Models.Job._DateChangeModel UpdateNonWarrantyAccountingChargePaid(Job job, string AccountingChargePaidDate) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -743,7 +743,7 @@ namespace Disco.Web.Areas.API.Controllers private Models.Job._DateChangeModel UpdateNonWarrantyPurchaseOrderRaised(Job job, string PurchaseOrderRaisedDate) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -786,7 +786,7 @@ namespace Disco.Web.Areas.API.Controllers private Models.Job._DateChangeModel UpdateNonWarrantyPurchaseOrderSent(Job job, string PurchaseOrderSentDate) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -820,7 +820,7 @@ namespace Disco.Web.Areas.API.Controllers private Models.Job._DateChangeModel UpdateNonWarrantyInvoiceReceived(Job job, string InvoiceReceivedDate) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -855,7 +855,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateNonWarrantyRepairerName(Job job, string RepairerName) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -873,7 +873,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateNonWarrantyRepairerLoggedDate(Job job, string RepairerLoggedDate) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -884,7 +884,7 @@ namespace Disco.Web.Areas.API.Controllers } else { - if (RepairerLoggedDate.Equals("Now", StringComparison.InvariantCultureIgnoreCase)) + if (RepairerLoggedDate.Equals("Now", StringComparison.OrdinalIgnoreCase)) { job.JobMetaNonWarranty.RepairerLoggedDate = DateTime.Now; } @@ -906,7 +906,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateNonWarrantyRepairerReference(Job job, string RepairerReference) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -924,7 +924,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateNonWarrantyRepairerCompletedDate(Job job, string RepairerCompletedDate) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -935,7 +935,7 @@ namespace Disco.Web.Areas.API.Controllers } else { - if (RepairerCompletedDate.Equals("Now", StringComparison.InvariantCultureIgnoreCase)) + if (RepairerCompletedDate.Equals("Now", StringComparison.OrdinalIgnoreCase)) { job.JobMetaNonWarranty.RepairerCompletedDate = DateTime.Now; } @@ -962,7 +962,7 @@ namespace Disco.Web.Areas.API.Controllers private Models.Job._DateChangeModel UpdateInsuranceClaimFormSentDate(Job job, string ClaimFormSentDate) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -973,7 +973,7 @@ namespace Disco.Web.Areas.API.Controllers } else { - if (ClaimFormSentDate.Equals("Now", StringComparison.InvariantCultureIgnoreCase)) + if (ClaimFormSentDate.Equals("Now", StringComparison.OrdinalIgnoreCase)) { job.JobMetaInsurance.ClaimFormSentDate = DateTime.Now; } @@ -1004,7 +1004,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsuranceDateOfPurchase(Job job, string DateOfPurchase) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1028,7 +1028,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsuranceOtherInterestedParties(Job job, string OtherInterestedParties) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1047,7 +1047,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsuranceRecoverReduceAction(Job job, string RecoverReduceAction) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1066,7 +1066,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsurancePoliceNotifiedCrimeReportNo(Job job, string PoliceNotifiedCrimeReportNo) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1085,7 +1085,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsurancePoliceNotifiedDate(Job job, string PoliceNotifiedDate) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1109,7 +1109,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsurancePoliceNotifiedStation(Job job, string PoliceNotifiedStation) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1128,7 +1128,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsurancePoliceNotified(Job job, string PoliceNotified) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1146,7 +1146,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsurancePropertyLastSeenDate(Job job, string PropertyLastSeenDate) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1173,7 +1173,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsuranceBurglaryTheftMethodOfEntry(Job job, string BurglaryTheftMethodOfEntry) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1192,7 +1192,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsuranceWitnessesNamesAddresses(Job job, string WitnessesNamesAddresses) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1211,7 +1211,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsuranceThirdPartyCausedWhy(Job job, string ThirdPartyCausedWhy) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1230,7 +1230,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsuranceThirdPartyCausedName(Job job, string ThirdPartyCausedName) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1249,7 +1249,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsuranceThirdPartyCaused(Job job, string ThirdPartyCaused) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1267,7 +1267,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsuranceDescription(Job job, string Description) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1286,7 +1286,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsuranceEventLocation(Job job, string EventLocation) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1305,7 +1305,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateInsuranceLossOrDamageDate(Job job, string LossOrDamageDate) { // Validate Is NonWarranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HNWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware NonWarranty Jobs"); } @@ -1334,7 +1334,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateWarrantyExternalName(Job job, string ExternalName) { // Validate Is Warranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware Warranty Jobs"); } @@ -1353,7 +1353,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateWarrantyExternalLoggedDate(Job job, string ExternalLoggedDate) { // Validate Is Warranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware Warranty Jobs"); } @@ -1380,7 +1380,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateWarrantyExternalReference(Job job, string ExternalReference) { // Validate Is Warranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware Warranty Jobs"); } @@ -1399,7 +1399,7 @@ namespace Disco.Web.Areas.API.Controllers private void UpdateWarrantyExternalCompletedDate(Job job, string ExternalCompletedDate) { // Validate Is Warranty Job - if (!job.JobTypeId.Equals(JobType.JobTypeIds.HWar, StringComparison.InvariantCultureIgnoreCase)) + if (!job.JobTypeId.Equals(JobType.JobTypeIds.HWar, StringComparison.OrdinalIgnoreCase)) { throw new Exception("This property can only be set for Hardware Warranty Jobs"); } @@ -1410,7 +1410,7 @@ namespace Disco.Web.Areas.API.Controllers } else { - if (ExternalCompletedDate.Equals("Now", StringComparison.InvariantCultureIgnoreCase)) + if (ExternalCompletedDate.Equals("Now", StringComparison.OrdinalIgnoreCase)) { job.JobMetaWarranty.ExternalCompletedDate = DateTime.Now; } @@ -1876,7 +1876,7 @@ namespace Disco.Web.Areas.API.Controllers var jl = Database.JobLogs.Find(id); if (jl != null) { - if (jl.TechUserId.Equals(CurrentUser.UserId, StringComparison.InvariantCultureIgnoreCase)) + if (jl.TechUserId.Equals(CurrentUser.UserId, StringComparison.OrdinalIgnoreCase)) Authorization.RequireAny(Claims.Job.Actions.RemoveAnyLogs, Claims.Job.Actions.RemoveOwnLogs); else Authorization.Require(Claims.Job.Actions.RemoveAnyLogs); @@ -1921,7 +1921,7 @@ namespace Disco.Web.Areas.API.Controllers var thumbFileInfo = new FileInfo(thumbPath); if (thumbFileInfo.Exists && thumbFileInfo.Length > 0) { - if (thumbPath.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase)) + if (thumbPath.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) return File(thumbPath, "image/png"); else return File(thumbPath, "image/jpg"); @@ -1944,7 +1944,7 @@ namespace Disco.Web.Areas.API.Controllers if (file.ContentLength > 0) { var contentType = file.ContentType; - if (string.IsNullOrEmpty(contentType) || contentType.Equals("unknown/unknown", StringComparison.InvariantCultureIgnoreCase)) + if (string.IsNullOrEmpty(contentType) || contentType.Equals("unknown/unknown", StringComparison.OrdinalIgnoreCase)) contentType = BI.Interop.MimeTypes.ResolveMimeType(file.FileName); var ja = new Disco.Models.Repository.JobAttachment() @@ -2012,7 +2012,7 @@ namespace Disco.Web.Areas.API.Controllers var ja = Database.JobAttachments.Include("TechUser").Where(m => m.Id == id).FirstOrDefault(); if (ja != null) { - if (ja.TechUserId.Equals(CurrentUser.UserId, StringComparison.InvariantCultureIgnoreCase)) + if (ja.TechUserId.Equals(CurrentUser.UserId, StringComparison.OrdinalIgnoreCase)) Authorization.RequireAny(Claims.Job.Actions.RemoveAnyAttachments, Claims.Job.Actions.RemoveOwnAttachments); else Authorization.Require(Claims.Job.Actions.RemoveAnyAttachments); @@ -2148,7 +2148,7 @@ namespace Disco.Web.Areas.API.Controllers { case Disco.Models.BI.Job.LocationModes.Unrestricted: var jobDateThreshold = DateTime.Now.AddYears(-1); - locations = Database.Jobs.Where(j => (j.OpenedDate > jobDateThreshold || !j.ClosedDate.HasValue) && j.DeviceHeldLocation != null).Select(j => j.DeviceHeldLocation).Distinct().OrderBy(l => l).ToList().Where(l => !string.IsNullOrWhiteSpace(l)).Select(l => l.Trim()).Distinct(StringComparer.InvariantCultureIgnoreCase).OrderBy(l => l).ToList(); + locations = Database.Jobs.Where(j => (j.OpenedDate > jobDateThreshold || !j.ClosedDate.HasValue) && j.DeviceHeldLocation != null).Select(j => j.DeviceHeldLocation).Distinct().OrderBy(l => l).ToList().Where(l => !string.IsNullOrWhiteSpace(l)).Select(l => l.Trim()).Distinct(StringComparer.OrdinalIgnoreCase).OrderBy(l => l).ToList(); break; case Disco.Models.BI.Job.LocationModes.OptionalList: case Disco.Models.BI.Job.LocationModes.RestrictedList: diff --git a/Disco.Web/Areas/API/Controllers/JobPreferencesController.cs b/Disco.Web/Areas/API/Controllers/JobPreferencesController.cs index 54018f07..812bf925 100644 --- a/Disco.Web/Areas/API/Controllers/JobPreferencesController.cs +++ b/Disco.Web/Areas/API/Controllers/JobPreferencesController.cs @@ -53,7 +53,7 @@ namespace Disco.Web.Areas.API.Controllers var list = LocationList .Where(i => !string.IsNullOrWhiteSpace(i)) .Select(i => i.Trim()) - .Distinct(StringComparer.InvariantCultureIgnoreCase) + .Distinct(StringComparer.OrdinalIgnoreCase) .OrderBy(i => i); Database.DiscoConfiguration.JobPreferences.LocationList = list.ToList(); @@ -93,7 +93,7 @@ namespace Disco.Web.Areas.API.Controllers list = list .Where(l => !string.IsNullOrWhiteSpace(l)) .Select(l => l.Trim()) - .Distinct(StringComparer.InvariantCultureIgnoreCase) + .Distinct(StringComparer.OrdinalIgnoreCase) .OrderBy(i => i); Database.DiscoConfiguration.JobPreferences.LocationList = list.ToList(); diff --git a/Disco.Web/Areas/API/Controllers/JobQueueController.cs b/Disco.Web/Areas/API/Controllers/JobQueueController.cs index f0e67ef7..27b3ad66 100644 --- a/Disco.Web/Areas/API/Controllers/JobQueueController.cs +++ b/Disco.Web/Areas/API/Controllers/JobQueueController.cs @@ -1,5 +1,4 @@ -using Disco.Models.Interop.ActiveDirectory; -using Disco.Models.Repository; +using Disco.Models.Repository; using Disco.Services.Authorization; using Disco.Services.Jobs.JobQueues; using Disco.Services.Web; @@ -291,14 +290,15 @@ namespace Disco.Web.Areas.API.Controllers var subjects = Subjects .Where(s => !string.IsNullOrWhiteSpace(s)) .Select(s => s.Trim()) - .Select(s => Tuple.Create(s, ActiveDirectory.RetrieveObject(s, Quick: true))) + .Select(s => Tuple.Create(s, ActiveDirectory.RetrieveADObject(s, Quick: true))) + .Where(s => s.Item2 is ADUserAccount || s.Item2 is ADGroup) .ToList(); var invalidSubjects = subjects.Where(s => s.Item2 == null).ToList(); if (invalidSubjects.Count > 0) throw new ArgumentException(string.Format("Subjects not found: {0}", string.Join(", ", invalidSubjects)), "Subjects"); - var proposedSubjects = subjects.Select(s => s.Item2.NetBiosId).OrderBy(s => s).ToArray(); + var proposedSubjects = subjects.Select(s => s.Item2.Id).OrderBy(s => s).ToArray(); subjectIds = string.Join(",", proposedSubjects); @@ -370,28 +370,5 @@ namespace Disco.Web.Areas.API.Controllers } } #endregion - - [DiscoAuthorize(Claims.Config.JobQueue.Configure)] - public virtual ActionResult SearchSubjects(string term) - { - var groupResults = ActiveDirectory.SearchGroups(term).Cast(); - var userResults = ActiveDirectory.SearchUserAccounts(term).Cast(); - - var results = groupResults.Concat(userResults).OrderBy(r => r.SamAccountName) - .Select(r => Models.JobQueue.SubjectItem.FromActiveDirectoryObject(r)).ToList(); - - return Json(results, JsonRequestBehavior.AllowGet); - } - - [DiscoAuthorize(Claims.Config.JobQueue.Configure)] - public virtual ActionResult Subject(string Id) - { - var subject = ActiveDirectory.RetrieveObject(Id, Quick: true); - - if (subject == null || !(subject is ActiveDirectoryUserAccount || subject is ActiveDirectoryGroup)) - return Json(null, JsonRequestBehavior.AllowGet); - else - return Json(Models.JobQueue.SubjectItem.FromActiveDirectoryObject(subject), JsonRequestBehavior.AllowGet); - } } } diff --git a/Disco.Web/Areas/API/Controllers/SystemController.cs b/Disco.Web/Areas/API/Controllers/SystemController.cs index 0d13e1a1..26216cbc 100644 --- a/Disco.Web/Areas/API/Controllers/SystemController.cs +++ b/Disco.Web/Areas/API/Controllers/SystemController.cs @@ -1,6 +1,5 @@ using Disco.BI.Extensions; using Disco.Data.Configuration; -using Disco.Models.Interop.ActiveDirectory; using Disco.Services.Authorization; using Disco.Services.Interop.ActiveDirectory; using Disco.Services.Web; @@ -20,7 +19,7 @@ namespace Disco.Web.Areas.API.Controllers [DiscoAuthorize(Claims.Config.System.Show)] public virtual ActionResult UpdateLastNetworkLogonDates() { - var taskStatus = Disco.Services.Interop.ActiveDirectory.Internal.ADUpdateLastNetworkLogonDateJob.ScheduleImmediately(); + var taskStatus = Disco.Services.Interop.ActiveDirectory.ADTaskUpdateNetworkLogonDates.ScheduleImmediately(); return RedirectToAction(MVC.Config.Logging.TaskStatus(taskStatus.SessionId)); } @@ -126,7 +125,7 @@ namespace Disco.Web.Areas.API.Controllers if (Image != null && Image.ContentLength > 0) { - if (Image.ContentType.StartsWith("image/", StringComparison.InvariantCultureIgnoreCase)) + if (Image.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) { Database.DiscoConfiguration.OrganisationLogo = Image.InputStream; @@ -227,7 +226,7 @@ namespace Disco.Web.Areas.API.Controllers [DiscoAuthorize(Claims.Config.System.ConfigureActiveDirectory)] public virtual ActionResult UpdateActiveDirectorySearchScope(List Containers, bool redirect = false) { - ActiveDirectory.UpdateSearchContainers(Database, Containers); + ActiveDirectory.Context.UpdateSearchContainers(Database, Containers); Database.SaveChanges(); if (redirect) @@ -237,17 +236,17 @@ namespace Disco.Web.Areas.API.Controllers } [DiscoAuthorize(Claims.Config.System.ConfigureActiveDirectory)] - public virtual ActionResult UpdateActiveDirectorySearchEntireForest(bool SearchEntireForest, bool redirect = false) + public virtual ActionResult UpdateActiveDirectorySearchAllForestServers(bool SearchAllForestServers, bool redirect = false) { try { - var result = ActiveDirectory.UpdateSearchEntireForest(Database, SearchEntireForest); + var result = ActiveDirectory.Context.UpdateSearchAllForestServers(Database, SearchAllForestServers); Database.SaveChanges(); if (!result) { - var forestServers = ActiveDirectory.LoadForestServers(); + var forestServers = ActiveDirectory.Context.ForestServers; if (forestServers.Count > ActiveDirectory.MaxForestServerSearch) throw new InvalidOperationException(string.Format("This forest contains more than the Max Forest Server Search restriction ({0})", ActiveDirectory.MaxForestServerSearch)); else @@ -271,8 +270,8 @@ namespace Disco.Web.Areas.API.Controllers [DiscoAuthorizeAny(Claims.Config.System.ConfigureActiveDirectory, Claims.Config.DeviceProfile.Configure)] public virtual ActionResult DomainOrganisationalUnits() { - var domainOUs = ActiveDirectory.Domains - .Select(d => new Models.System.DomainOrganisationalUnitsModel() { Domain = d, OrganisationalUnits = ActiveDirectory.RetrieveOrganisationalUnitStructure(d) }) + var domainOUs = ActiveDirectory.RetrieveADOrganisationalUnitStructure() + .Select(d => new Models.System.DomainOrganisationalUnitsModel() { Domain = d.Item1, OrganisationalUnits = d.Item2}) .Select(ous => ous.ToFancyTreeNode()).ToList(); return new JsonResult() @@ -283,6 +282,29 @@ namespace Disco.Web.Areas.API.Controllers }; } + [DiscoAuthorizeAny(Claims.DiscoAdminAccount, Claims.Config.JobQueue.Configure)] + public virtual ActionResult SearchSubjects(string term) + { + var groupResults = ActiveDirectory.SearchADGroups(term).Cast(); + var userResults = ActiveDirectory.SearchADUserAccounts(term, true).Cast(); + + var results = groupResults.Concat(userResults).OrderBy(r => r.SamAccountName) + .Select(r => Models.Shared.SubjectDescriptorModel.FromActiveDirectoryObject(r)).ToList(); + + return Json(results, JsonRequestBehavior.AllowGet); + } + + [DiscoAuthorizeAny(Claims.DiscoAdminAccount, Claims.Config.JobQueue.Configure)] + public virtual ActionResult Subject(string Id) + { + var subject = ActiveDirectory.RetrieveADObject(Id, Quick: true); + + if (subject == null) + return Json(null, JsonRequestBehavior.AllowGet); + else + return Json(Models.Shared.SubjectDescriptorModel.FromActiveDirectoryObject(subject), JsonRequestBehavior.AllowGet); + } + #endregion #region Proxy Settings diff --git a/Disco.Web/Areas/API/Controllers/UserController.cs b/Disco.Web/Areas/API/Controllers/UserController.cs index 4c400c9a..b36b27b9 100644 --- a/Disco.Web/Areas/API/Controllers/UserController.cs +++ b/Disco.Web/Areas/API/Controllers/UserController.cs @@ -50,7 +50,7 @@ namespace Disco.Web.Areas.API.Controllers var thumbPath = ua.RepositoryThumbnailFilename(Database); if (System.IO.File.Exists(thumbPath)) { - if (thumbPath.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase)) + if (thumbPath.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) return File(thumbPath, "image/png"); else return File(thumbPath, "image/jpg"); @@ -65,7 +65,7 @@ namespace Disco.Web.Areas.API.Controllers public virtual ActionResult AttachmentUpload(string id, string Domain, string Comments) { if (string.IsNullOrEmpty(Domain)) - id = ActiveDirectory.PrimaryDomain.NetBiosName + @"\" + id; + id = ActiveDirectory.Context.PrimaryDomain.NetBiosName + @"\" + id; else id = Domain + @"\" + id; @@ -78,7 +78,7 @@ namespace Disco.Web.Areas.API.Controllers if (file.ContentLength > 0) { var contentType = file.ContentType; - if (string.IsNullOrEmpty(contentType) || contentType.Equals("unknown/unknown", StringComparison.InvariantCultureIgnoreCase)) + if (string.IsNullOrEmpty(contentType) || contentType.Equals("unknown/unknown", StringComparison.OrdinalIgnoreCase)) contentType = BI.Interop.MimeTypes.ResolveMimeType(file.FileName); var ua = new Disco.Models.Repository.UserAttachment() @@ -127,7 +127,7 @@ namespace Disco.Web.Areas.API.Controllers public virtual ActionResult Attachments(string id, string Domain) { if (string.IsNullOrEmpty(Domain)) - id = ActiveDirectory.PrimaryDomain.NetBiosName + @"\" + id; + id = ActiveDirectory.Context.PrimaryDomain.NetBiosName + @"\" + id; else id = Domain + @"\" + id; @@ -151,7 +151,7 @@ namespace Disco.Web.Areas.API.Controllers var ua = Database.UserAttachments.Include("TechUser").Where(m => m.Id == id).FirstOrDefault(); if (ua != null) { - if (ua.TechUserId.Equals(CurrentUser.UserId, StringComparison.InvariantCultureIgnoreCase)) + if (ua.TechUserId.Equals(CurrentUser.UserId, StringComparison.OrdinalIgnoreCase)) Authorization.RequireAny(Claims.User.Actions.RemoveAnyAttachments, Claims.User.Actions.RemoveOwnAttachments); else Authorization.Require(Claims.User.Actions.RemoveAnyAttachments); @@ -174,7 +174,7 @@ namespace Disco.Web.Areas.API.Controllers throw new ArgumentNullException("AttachmentTypeId"); if (string.IsNullOrEmpty(Domain)) - id = ActiveDirectory.PrimaryDomain.NetBiosName + @"\" + id; + id = ActiveDirectory.Context.PrimaryDomain.NetBiosName + @"\" + id; else id = Domain + @"\" + id; diff --git a/Disco.Web/Areas/API/Models/AuthorizationRole/SubjectItem.cs b/Disco.Web/Areas/API/Models/AuthorizationRole/SubjectItem.cs deleted file mode 100644 index cd71d093..00000000 --- a/Disco.Web/Areas/API/Models/AuthorizationRole/SubjectItem.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Disco.Models.Interop.ActiveDirectory; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; - -namespace Disco.Web.Areas.API.Models.AuthorizationRole -{ - public class SubjectItem - { - public string Id { get; set; } - public string Name { get; set; } - public string Type { get; set; } - - public static SubjectItem FromActiveDirectoryObject(IActiveDirectoryObject ADObject) - { - return new Models.AuthorizationRole.SubjectItem() - { - Id = ADObject.NetBiosId, - Name = ADObject.DisplayName, - Type = ADObject is ActiveDirectoryGroup ? "group" : "user" - }; - } - } -} \ No newline at end of file diff --git a/Disco.Web/Areas/API/Models/JobQueue/SubjectItem.cs b/Disco.Web/Areas/API/Models/JobQueue/SubjectItem.cs deleted file mode 100644 index 309f1168..00000000 --- a/Disco.Web/Areas/API/Models/JobQueue/SubjectItem.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Disco.Models.Interop.ActiveDirectory; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; - -namespace Disco.Web.Areas.API.Models.JobQueue -{ - public class SubjectItem - { - public string Id { get; set; } - public string Name { get; set; } - public string Type { get; set; } - - public static SubjectItem FromActiveDirectoryObject(IActiveDirectoryObject ADObject) - { - return new Models.JobQueue.SubjectItem() - { - Id = ADObject.NetBiosId, - Name = ADObject.DisplayName, - Type = ADObject is ActiveDirectoryGroup ? "group" : "user" - }; - } - } -} \ No newline at end of file diff --git a/Disco.Web/Areas/API/Models/Shared/SubjectDescriptorModel.cs b/Disco.Web/Areas/API/Models/Shared/SubjectDescriptorModel.cs new file mode 100644 index 00000000..4b672980 --- /dev/null +++ b/Disco.Web/Areas/API/Models/Shared/SubjectDescriptorModel.cs @@ -0,0 +1,51 @@ +using Disco.Services.Interop.ActiveDirectory; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; + +namespace Disco.Web.Areas.API.Models.Shared +{ + public class SubjectDescriptorModel + { + public string Id { get; set; } + + public string Name { get; set; } + public string Type { get; set; } + + public bool IsGroup { get; set; } + public bool IsUserAccount { get; set; } + public bool IsMachineAccount { get; set; } + + public static SubjectDescriptorModel FromActiveDirectoryObject(IADObject ADObject) + { + var item = new SubjectDescriptorModel() + { + Id = ADObject.Id, + Name = ADObject.DisplayName + }; + + if (ADObject is ADUserAccount) + { + item.IsUserAccount = true; + item.Type = "user"; + } + else if (ADObject is ADGroup) + { + item.IsGroup = true; + item.Type = "group"; + } + else if (ADObject is ADMachineAccount) + { + item.IsMachineAccount = true; + item.Type = "machine"; + } + else + { + item.Type = "unknown"; + } + + return item; + } + } +} \ No newline at end of file diff --git a/Disco.Web/Areas/API/Models/System/DomainOrganisationalUnitsModel.cs b/Disco.Web/Areas/API/Models/System/DomainOrganisationalUnitsModel.cs index c17b8aa2..36a88980 100644 --- a/Disco.Web/Areas/API/Models/System/DomainOrganisationalUnitsModel.cs +++ b/Disco.Web/Areas/API/Models/System/DomainOrganisationalUnitsModel.cs @@ -1,16 +1,14 @@ -using Disco.Models.Interop.ActiveDirectory; +using Disco.Services.Interop.ActiveDirectory; using Disco.Web.Models.Shared; -using System; using System.Collections.Generic; using System.Linq; -using System.Web; namespace Disco.Web.Areas.API.Models.System { public class DomainOrganisationalUnitsModel { - public ActiveDirectoryDomain Domain { get; set; } - public List OrganisationalUnits { get; set; } + public ADDomain Domain { get; set; } + public List OrganisationalUnits { get; set; } public FancyTreeNode ToFancyTreeNode() { @@ -21,13 +19,13 @@ namespace Disco.Web.Areas.API.Models.System key = Domain.DistinguishedName, title = Domain.NetBiosName, folder = true, - tooltip = Domain.DnsName, + tooltip = Domain.Name, children = children, unselectable = false, expanded = true }; } - private FancyTreeNode OrganisationalUnitToFancyTreeNode(ActiveDirectoryOrganisationalUnit OrganisationalUnit) + private FancyTreeNode OrganisationalUnitToFancyTreeNode(ADOrganisationalUnit OrganisationalUnit) { FancyTreeNode[] children = OrganisationalUnit.Children == null ? null diff --git a/Disco.Web/Areas/Config/Controllers/AuthorizationRoleController.cs b/Disco.Web/Areas/Config/Controllers/AuthorizationRoleController.cs index 4434e96e..a4663679 100644 --- a/Disco.Web/Areas/Config/Controllers/AuthorizationRoleController.cs +++ b/Disco.Web/Areas/Config/Controllers/AuthorizationRoleController.cs @@ -6,6 +6,7 @@ using Disco.Services.Interop.ActiveDirectory; using Disco.Services.Plugins.Features.UIExtension; using Disco.Services.Users; using Disco.Services.Web; +using Disco.Web.Areas.API.Models.Shared; using System; using System.Collections.Generic; using System.Linq; @@ -27,10 +28,10 @@ namespace Disco.Web.Areas.Config.Controllers throw new ArgumentException("Invalid Authorization Role Id"); var token = RoleToken.FromAuthorizationRole(ar); - var subjects = token.SubjectIds == null ? new List() : - token.SubjectIds.Select(subjectId => ActiveDirectory.RetrieveObject(subjectId, Quick: true)) + var subjects = token.SubjectIds == null ? new List() : + token.SubjectIds.Select(subjectId => ActiveDirectory.RetrieveADObject(subjectId, Quick: true)) .Where(item => item != null) - .Select(item => Models.AuthorizationRole.SubjectDescriptorModel.FromActiveDirectoryObject(item)) + .Select(item => SubjectDescriptorModel.FromActiveDirectoryObject(item)) .OrderBy(item => item.Name).ToList(); var m = new Models.AuthorizationRole.ShowModel() @@ -53,9 +54,9 @@ namespace Disco.Web.Areas.Config.Controllers .Select(ar => RoleToken.FromAuthorizationRole(ar)).Cast().ToList(); var administratorSubjects = UserService.AdministratorSubjectIds - .Select(subjectId => ActiveDirectory.RetrieveObject(subjectId, Quick: true)) + .Select(subjectId => ActiveDirectory.RetrieveADObject(subjectId, Quick: true)) .Where(item => item != null) - .Select(item => Models.AuthorizationRole.SubjectDescriptorModel.FromActiveDirectoryObject(item)) + .Select(item => SubjectDescriptorModel.FromActiveDirectoryObject(item)) .OrderBy(item => item.Name).ToList(); var m = new Models.AuthorizationRole.IndexModel() diff --git a/Disco.Web/Areas/Config/Controllers/DeviceProfileController.cs b/Disco.Web/Areas/Config/Controllers/DeviceProfileController.cs index 8ed208b0..4cdc329f 100644 --- a/Disco.Web/Areas/Config/Controllers/DeviceProfileController.cs +++ b/Disco.Web/Areas/Config/Controllers/DeviceProfileController.cs @@ -77,7 +77,7 @@ namespace Disco.Web.Areas.Config.Controllers ComputerNameTemplate = DeviceProfile.DefaultComputerNameTemplate, ProvisionADAccount = true, DistributionType = DeviceProfile.DistributionTypes.OneToMany, - OrganisationalUnit = ActiveDirectory.PrimaryDomain.GetDefaultComputerContainer() + OrganisationalUnit = ActiveDirectory.Context.PrimaryDomain.DefaultComputerContainer } }; diff --git a/Disco.Web/Areas/Config/Controllers/JobQueueController.cs b/Disco.Web/Areas/Config/Controllers/JobQueueController.cs index f5d8ebd4..14083474 100644 --- a/Disco.Web/Areas/Config/Controllers/JobQueueController.cs +++ b/Disco.Web/Areas/Config/Controllers/JobQueueController.cs @@ -6,6 +6,7 @@ using Disco.Services.Interop.ActiveDirectory; using Disco.Services.Jobs.JobQueues; using Disco.Services.Plugins.Features.UIExtension; using Disco.Services.Web; +using Disco.Web.Areas.API.Models.Shared; using System; using System.Collections.Generic; using System.Linq; @@ -28,10 +29,10 @@ namespace Disco.Web.Areas.Config.Controllers throw new ArgumentException("Invalid Job Queue Id"); var token = JobQueueToken.FromJobQueue(jq); - var subjects = token.SubjectIds == null ? new List() : - token.SubjectIds.Select(subjectId => ActiveDirectory.RetrieveObject(subjectId, Quick: true)) + var subjects = token.SubjectIds == null ? new List() : + token.SubjectIds.Select(subjectId => ActiveDirectory.RetrieveADObject(subjectId, Quick: true)) .Where(item => item != null) - .Select(item => Models.JobQueue.ShowModel.SubjectDescriptor.FromActiveDirectoryObject(item)) + .Select(item => SubjectDescriptorModel.FromActiveDirectoryObject(item)) .OrderBy(item => item.Name).ToList(); var totalJobCount = Database.JobQueueJobs.Where(jqj => jqj.JobQueueId == id.Value).Select(jqj => jqj.Job).Distinct().Count(); diff --git a/Disco.Web/Areas/Config/Models/AuthorizationRole/IndexModel.cs b/Disco.Web/Areas/Config/Models/AuthorizationRole/IndexModel.cs index 41bfdf62..5c882ba3 100644 --- a/Disco.Web/Areas/Config/Models/AuthorizationRole/IndexModel.cs +++ b/Disco.Web/Areas/Config/Models/AuthorizationRole/IndexModel.cs @@ -5,6 +5,7 @@ using System.Web; using Disco.Data.Repository; using Disco.Models.UI.Config.AuthorizationRole; using Disco.Models.Services.Authorization; +using Disco.Web.Areas.API.Models.Shared; namespace Disco.Web.Areas.Config.Models.AuthorizationRole { diff --git a/Disco.Web/Areas/Config/Models/AuthorizationRole/ShowModel.cs b/Disco.Web/Areas/Config/Models/AuthorizationRole/ShowModel.cs index 4db7f9aa..63d13b25 100644 --- a/Disco.Web/Areas/Config/Models/AuthorizationRole/ShowModel.cs +++ b/Disco.Web/Areas/Config/Models/AuthorizationRole/ShowModel.cs @@ -1,6 +1,6 @@ using Disco.Models.Services.Authorization; -using Disco.Models.Interop.ActiveDirectory; using Disco.Models.UI.Config.AuthorizationRole; +using Disco.Web.Areas.API.Models.Shared; using Disco.Web.Models.Shared; using System; using System.Collections.Generic; diff --git a/Disco.Web/Areas/Config/Models/AuthorizationRole/SubjectDescriptorModel.cs b/Disco.Web/Areas/Config/Models/AuthorizationRole/SubjectDescriptorModel.cs deleted file mode 100644 index b8d1481e..00000000 --- a/Disco.Web/Areas/Config/Models/AuthorizationRole/SubjectDescriptorModel.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Disco.Models.Interop.ActiveDirectory; - -namespace Disco.Web.Areas.Config.Models.AuthorizationRole -{ - public class SubjectDescriptorModel - { - public bool IsGroup { get; set; } - public string Name { get; set; } - public string Id { get; set; } - - public static SubjectDescriptorModel FromActiveDirectoryObject(IActiveDirectoryObject ADObject) - { - var item = new SubjectDescriptorModel() - { - Id = ADObject.NetBiosId, - Name = ADObject.DisplayName - }; - - if (ADObject is ActiveDirectoryGroup) - item.IsGroup = true; - - return item; - } - } -} \ No newline at end of file diff --git a/Disco.Web/Areas/Config/Models/DeviceProfile/ShowModel.cs b/Disco.Web/Areas/Config/Models/DeviceProfile/ShowModel.cs index c4fe28c8..06a77bfd 100644 --- a/Disco.Web/Areas/Config/Models/DeviceProfile/ShowModel.cs +++ b/Disco.Web/Areas/Config/Models/DeviceProfile/ShowModel.cs @@ -5,6 +5,7 @@ using System.Web; using System.Web.Mvc; using Disco.Services.Plugins; using Disco.Models.UI.Config.DeviceProfile; +using Disco.Services.Interop.ActiveDirectory; namespace Disco.Web.Areas.Config.Models.DeviceProfile { @@ -15,6 +16,23 @@ namespace Disco.Web.Areas.Config.Models.DeviceProfile public Disco.Models.BI.Config.OrganisationAddress DefaultOrganisationAddress { get; set; } public List OrganisationAddresses { get; set; } + public string FriendlyOrganisationalUnitName + { + get + { + if (string.IsNullOrEmpty(this.DeviceProfile.OrganisationalUnit)) + { + var domain = ActiveDirectory.Context.PrimaryDomain; + return domain.FriendlyDistinguishedNamePath(domain.DefaultComputerContainer); + } + else + { + var domain = ActiveDirectory.Context.GetDomainFromDistinguishedName(this.DeviceProfile.OrganisationalUnit); + return domain.FriendlyDistinguishedNamePath(this.DeviceProfile.OrganisationalUnit); + } + } + } + public List CertificateProviders { get; set; } public int DeviceCount { get; set; } diff --git a/Disco.Web/Areas/Config/Models/JobQueue/ShowModel.cs b/Disco.Web/Areas/Config/Models/JobQueue/ShowModel.cs index bc6619b5..0ec23b33 100644 --- a/Disco.Web/Areas/Config/Models/JobQueue/ShowModel.cs +++ b/Disco.Web/Areas/Config/Models/JobQueue/ShowModel.cs @@ -1,10 +1,7 @@ -using Disco.Models.Interop.ActiveDirectory; -using Disco.Models.Services.Jobs.JobQueues; +using Disco.Models.Services.Jobs.JobQueues; using Disco.Models.UI.Config.JobQueue; -using System; +using Disco.Web.Areas.API.Models.Shared; using System.Collections.Generic; -using System.Linq; -using System.Web; namespace Disco.Web.Areas.Config.Models.JobQueue { @@ -12,28 +9,7 @@ namespace Disco.Web.Areas.Config.Models.JobQueue { public IJobQueueToken Token { get; set; } - public List Subjects { get; set; } - - public class SubjectDescriptor - { - public bool IsGroup { get; set; } - public string Name { get; set; } - public string Id { get; set; } - - public static SubjectDescriptor FromActiveDirectoryObject(IActiveDirectoryObject ADObject) - { - var item = new SubjectDescriptor() - { - Id = ADObject.NetBiosId, - Name = ADObject.DisplayName - }; - - if (ADObject is ActiveDirectoryGroup) - item.IsGroup = true; - - return item; - } - } + public List Subjects { get; set; } public int OpenJobCount { get; set; } public int TotalJobCount { get; set; } @@ -41,6 +17,5 @@ namespace Disco.Web.Areas.Config.Models.JobQueue public List JobTypes { get; set; } public bool CanDelete { get; set; } - } } \ No newline at end of file diff --git a/Disco.Web/Areas/Config/Models/SystemConfig/IndexModel.cs b/Disco.Web/Areas/Config/Models/SystemConfig/IndexModel.cs index ac97b6c8..8c3dad5f 100644 --- a/Disco.Web/Areas/Config/Models/SystemConfig/IndexModel.cs +++ b/Disco.Web/Areas/Config/Models/SystemConfig/IndexModel.cs @@ -8,7 +8,6 @@ using System.Data.SqlClient; using Disco.Data.Repository; using Disco.Models.BI.Interop.Community; using Disco.Services.Tasks; -using Disco.Models.Interop.ActiveDirectory; using System.DirectoryServices.ActiveDirectory; using Disco.Services.Interop.ActiveDirectory; @@ -77,14 +76,14 @@ namespace Disco.Web.Areas.Config.Models.SystemConfig #region Active Directory - [Display(Name="Search Entire Forest")] - public bool ADSearchEntireForest { get; set; } + [Display(Name="Search All Forest Servers")] + public bool ADSearchAllForestServers { get; set; } - public ActiveDirectoryDomain ADPrimaryDomain { get; set; } - public List ADAdditionalDomains { get; set; } - public ActiveDirectorySite ADSite { get; set; } - public List> ADSiteServers { get; set; } - public List> ADSearchContainers { get; set; } + public List ADDomains { get; set; } + public ADDomain ADPrimaryDomain { get; set; } + public ADSite ADSite { get; set; } + public List ADServers { get; set; } + public List> ADSearchContainers { get; set; } public List ADForestServers { get; set; } #endregion @@ -119,28 +118,28 @@ namespace Disco.Web.Areas.Config.Models.SystemConfig }; // AD - m.ADPrimaryDomain = ActiveDirectory.PrimaryDomain; - m.ADAdditionalDomains = ActiveDirectory.Domains.Where(d => d != m.ADPrimaryDomain).ToList(); - m.ADSite = ActiveDirectory.Site; - m.ADSiteServers = m.ADSite.Servers.Cast().Select(s => Tuple.Create(s, s.Reachable())).ToList(); + m.ADDomains = ActiveDirectory.Context.Domains.ToList(); + m.ADPrimaryDomain = ActiveDirectory.Context.PrimaryDomain; + m.ADSite = ActiveDirectory.Context.Site; + m.ADServers = ActiveDirectory.Context.Domains.SelectMany(d => d.DomainControllers).ToList(); var configSearchContainers = config.ActiveDirectory.SearchContainers; m.ADSearchContainers = configSearchContainers == null ? null : configSearchContainers.SelectMany(d => d.Value, (k, c) => { - var domain = ActiveDirectory.GetDomainByDnsName(k.Key); - return Tuple.Create(c, domain, domain.GetFriendlyOrganisationalUnitName(c)); + var domain = ActiveDirectory.Context.GetDomainByName(k.Key); + return Tuple.Create(c, domain, domain.FriendlyDistinguishedNamePath(c)); }).ToList(); - var loadForestServersTask = ActiveDirectory.LoadForestServersAsync(); + var loadForestServersTask = ADDiscoverForestServers.LoadForestServersAsync(); if (loadForestServersTask.Wait(TimeSpan.FromSeconds(1))) { m.ADForestServers = loadForestServersTask.Result; - var configValue = config.ActiveDirectory.SearchEntireForest ?? true; - m.ADSearchEntireForest = configValue && m.ADForestServers.Count <= ActiveDirectory.MaxForestServerSearch; + var configValue = config.ActiveDirectory.SearchAllForestServers ?? true; + m.ADSearchAllForestServers = configValue && m.ADForestServers.Count <= ActiveDirectory.MaxForestServerSearch; } else { m.ADForestServers = null; - m.ADSearchEntireForest = config.ActiveDirectory.SearchEntireForest ?? true; + m.ADSearchAllForestServers = config.ActiveDirectory.SearchAllForestServers ?? true; } return m; diff --git a/Disco.Web/Areas/Config/Views/AuthorizationRole/Index.cshtml b/Disco.Web/Areas/Config/Views/AuthorizationRole/Index.cshtml index b26bf849..99fa9c69 100644 --- a/Disco.Web/Areas/Config/Views/AuthorizationRole/Index.cshtml +++ b/Disco.Web/Areas/Config/Views/AuthorizationRole/Index.cshtml @@ -90,7 +90,7 @@ else textAdd.watermark('Search Subjects') .autocomplete({ - source: '@(Url.Action(MVC.API.AuthorizationRole.SearchSubjects()))', + source: '@(Url.Action(MVC.API.System.SearchSubjects()))', minLength: 2, focus: function (e, ui) { textAdd.val(ui.item.Id); @@ -148,32 +148,37 @@ else var id = textAdd.val(); $.ajax({ - url: '@Url.Action(MVC.API.AuthorizationRole.Subject())', + url: '@Url.Action(MVC.API.System.Subject())', method: 'post', data: { Id: id } }).done(function (response) { if (response) { - if (list.find('li[data-subjectid="' + response.Id.replace('\\', '\\\\') + '"]').length == 0) { + if (response.IsGroup || response.IsUserAccount) { + if (list.find('li[data-subjectid="' + response.Id.replace('\\', '\\\\') + '"]').length == 0) { - var liIcon = $('').addClass('fa fa-lg'); - if (response.Type === 'user') - liIcon.addClass('fa-user'); - else - liIcon.addClass('fa-users'); + var liIcon = $('').addClass('fa fa-lg'); + if (response.Type === 'user') + liIcon.addClass('fa-user'); + else + liIcon.addClass('fa-users'); - var li = $('
  • ') - .append(liIcon) - .append($('').text(response.Id == response.Name ? response.Id : response.Name + ' [' + response.Id + ']')) - .append($('').addClass('fa fa-times-circle remove')) - .addClass(response.Type) - .attr('data-subjectid', response.Id) - .attr('data-subjectstatus', 'new'); + var li = $('
  • ') + .append(liIcon) + .append($('').text(response.Id == response.Name ? response.Id : response.Name + ' [' + response.Id + ']')) + .append($('').addClass('fa fa-times-circle remove')) + .addClass(response.Type) + .attr('data-subjectid', response.Id) + .attr('data-subjectstatus', 'new'); - list.append(li); + list.append(li); - updateNoSubjects(); - } else { - alert('That subject has already been added'); + updateNoSubjects(); + } else { + alert('That subject has already been added'); + } + } + else { + alert(response.Name + ' ['+response.Id+'] is a ' + response.Type + '. Only users and groups can be added.'); } } else { alert('Unknown Id'); diff --git a/Disco.Web/Areas/Config/Views/AuthorizationRole/Index.generated.cs b/Disco.Web/Areas/Config/Views/AuthorizationRole/Index.generated.cs index b5116f03..c17229bc 100644 --- a/Disco.Web/Areas/Config/Views/AuthorizationRole/Index.generated.cs +++ b/Disco.Web/Areas/Config/Views/AuthorizationRole/Index.generated.cs @@ -385,7 +385,7 @@ WriteLiteral(@"> #line 93 "..\..\Areas\Config\Views\AuthorizationRole\Index.cshtml" - Write(Url.Action(MVC.API.AuthorizationRole.SearchSubjects())); + Write(Url.Action(MVC.API.System.SearchSubjects())); #line default @@ -417,42 +417,47 @@ WriteLiteral("\',\r\n minLength: 2,\r\n #line 151 "..\..\Areas\Config\Views\AuthorizationRole\Index.cshtml" - Write(Url.Action(MVC.API.AuthorizationRole.Subject())); + Write(Url.Action(MVC.API.System.Subject())); #line default #line hidden WriteLiteral("\',\r\n method: \'post\',\r\n data: { Id: id }\r\n " + " }).done(function (response) {\r\n if (response) {\r\n " + -" if (list.find(\'li[data-subjectid=\"\' + response.Id.replace(\'\\\\\', \'\\\\\\\\\') + " + -"\'\"]\').length == 0) {\r\n\r\n var liIcon = $(\'\').addClass(\'" + -"fa fa-lg\');\r\n if (response.Type === \'user\')\r\n " + -" liIcon.addClass(\'fa-user\');\r\n else\r\n " + -" liIcon.addClass(\'fa-users\');\r\n\r\n v" + -"ar li = $(\'
  • \')\r\n .append(liIcon)\r\n " + -" .append($(\'\').text(response.Id == response.Name ? response.Id " + -": response.Name + \' [\' + response.Id + \']\'))\r\n .appen" + -"d($(\'\').addClass(\'fa fa-times-circle remove\'))\r\n ." + -"addClass(response.Type)\r\n .attr(\'data-subjectid\', res" + -"ponse.Id)\r\n .attr(\'data-subjectstatus\', \'new\');\r\n\r\n " + -" list.append(li);\r\n\r\n updateNoSubjec" + -"ts();\r\n } else {\r\n alert(\'That subject" + -" has already been added\');\r\n }\r\n } else {\r\n " + -" alert(\'Unknown Id\');\r\n }\r\n }).fail(fu" + -"nction (jqXHR, textStatus, errorThrown) {\r\n alert(\'Error: \' + err" + -"orThrown);\r\n });\r\n return false;\r\n }\r\n\r\n " + -" function updateNoSubjects() {\r\n if (list.find(\'li:visible\').leng" + -"th > 0)\r\n noSubjects.hide();\r\n else\r\n n" + -"oSubjects.show();\r\n }\r\n\r\n function saveChanges() {\r\n va" + -"r form = $(\'#Config_AuthRoles_Subjects_Update_Dialog_Form\').empty();\r\n\r\n " + -" list.find(\'li[data-subjectstatus!=\"removed\"]\').each(function () {\r\n " + -" var subjectId = $(this).attr(\'data-subjectid\');\r\n\r\n form.a" + -"ppend($(\'\').attr({\r\n \'name\': \'Subjects\',\r\n " + -" \'type\': \'hidden\'\r\n }).val(subjectId));\r\n\r\n })." + -"get();\r\n\r\n form.submit();\r\n\r\n dialog.dialog(\"disable\");\r\n " + -" dialog.dialog(\"option\", \"buttons\", null);\r\n }\r\n\r\n $(fun" + -"ction () {\r\n $(\'#Config_AuthRoles_UpdateAdministrators\').click(showDi" + -"alog);\r\n });\r\n\r\n })();\r\n\r\n\r\n\').addClass(\'f" + +"a fa-lg\');\r\n if (response.Type === \'user\')\r\n " + +" liIcon.addClass(\'fa-user\');\r\n " + +"else\r\n liIcon.addClass(\'fa-users\');\r\n\r\n " + +" var li = $(\'
  • \')\r\n .append(li" + +"Icon)\r\n .append($(\'\').text(response.Id == r" + +"esponse.Name ? response.Id : response.Name + \' [\' + response.Id + \']\'))\r\n " + +" .append($(\'\').addClass(\'fa fa-times-circle remove\'))" + +"\r\n .addClass(response.Type)\r\n " + +" .attr(\'data-subjectid\', response.Id)\r\n " + +" .attr(\'data-subjectstatus\', \'new\');\r\n\r\n list.append" + +"(li);\r\n\r\n updateNoSubjects();\r\n " + +" } else {\r\n alert(\'That subject has already been add" + +"ed\');\r\n }\r\n }\r\n els" + +"e {\r\n alert(response.Name + \' [\'+response.Id+\'] is a \' + " + +"response.Type + \'. Only users and groups can be added.\');\r\n }" + +"\r\n } else {\r\n alert(\'Unknown Id\');\r\n " + +" }\r\n }).fail(function (jqXHR, textStatus, errorThrown) {\r\n " + +" alert(\'Error: \' + errorThrown);\r\n });\r\n retu" + +"rn false;\r\n }\r\n\r\n function updateNoSubjects() {\r\n i" + +"f (list.find(\'li:visible\').length > 0)\r\n noSubjects.hide();\r\n " + +" else\r\n noSubjects.show();\r\n }\r\n\r\n function " + +"saveChanges() {\r\n var form = $(\'#Config_AuthRoles_Subjects_Update_Dia" + +"log_Form\').empty();\r\n\r\n list.find(\'li[data-subjectstatus!=\"removed\"]\'" + +").each(function () {\r\n var subjectId = $(this).attr(\'data-subject" + +"id\');\r\n\r\n form.append($(\'\').attr({\r\n \'n" + +"ame\': \'Subjects\',\r\n \'type\': \'hidden\'\r\n }).val(" + +"subjectId));\r\n\r\n }).get();\r\n\r\n form.submit();\r\n\r\n " + +" dialog.dialog(\"disable\");\r\n dialog.dialog(\"option\", \"buttons\", nul" + +"l);\r\n }\r\n\r\n $(function () {\r\n $(\'#Config_AuthRoles_Upda" + +"teAdministrators\').click(showDialog);\r\n });\r\n\r\n })();\r\n\r\n\r\nUpdate Disco Administrators ["); - #line 221 "..\..\Areas\Config\Views\AuthorizationRole\Index.cshtml" + #line 226 "..\..\Areas\Config\Views\AuthorizationRole\Index.cshtml" Write(Model.AdministratorSubjects.Count); @@ -478,7 +483,7 @@ WriteLiteral("]\r\n"); WriteLiteral(" "); - #line 222 "..\..\Areas\Config\Views\AuthorizationRole\Index.cshtml" + #line 227 "..\..\Areas\Config\Views\AuthorizationRole\Index.cshtml" Write(Html.ActionLinkButton("Create Authorization Role", MVC.Config.AuthorizationRole.Create())); diff --git a/Disco.Web/Areas/Config/Views/AuthorizationRole/Show.cshtml b/Disco.Web/Areas/Config/Views/AuthorizationRole/Show.cshtml index 5c0cad9b..a86e2b28 100644 --- a/Disco.Web/Areas/Config/Views/AuthorizationRole/Show.cshtml +++ b/Disco.Web/Areas/Config/Views/AuthorizationRole/Show.cshtml @@ -112,7 +112,7 @@ textAdd.watermark('Search Subjects') .autocomplete({ - source: '@(Url.Action(MVC.API.AuthorizationRole.SearchSubjects()))', + source: '@(Url.Action(MVC.API.System.SearchSubjects()))', minLength: 2, focus: function (e, ui) { textAdd.val(ui.item.Id); @@ -169,32 +169,36 @@ var id = textAdd.val(); $.ajax({ - url: '@Url.Action(MVC.API.AuthorizationRole.Subject())', + url: '@Url.Action(MVC.API.System.Subject())', method: 'post', data: { Id: id } }).done(function(response){ if (response){ - if (list.find('li[data-subjectid="'+response.Id.replace('\\', '\\\\')+'"]').length == 0){ + if (response.IsGroup || response.IsUserAccount) { + if (list.find('li[data-subjectid="'+response.Id.replace('\\', '\\\\')+'"]').length == 0){ - var liIcon = $('').addClass('fa fa-lg'); - if (response.Type === 'user') - liIcon.addClass('fa-user'); - else - liIcon.addClass('fa-users'); + var liIcon = $('').addClass('fa fa-lg'); + if (response.Type === 'user') + liIcon.addClass('fa-user'); + else + liIcon.addClass('fa-users'); - var li = $('
  • ') - .append(liIcon) - .append($('').text(response.Id == response.Name ? response.Id : response.Name + ' [' + response.Id + ']')) - .append($('').addClass('fa fa-times-circle remove')) - .addClass(response.Type) - .attr('data-subjectid', response.Id) - .attr('data-subjectstatus', 'new'); + var li = $('
  • ') + .append(liIcon) + .append($('').text(response.Id == response.Name ? response.Id : response.Name + ' [' + response.Id + ']')) + .append($('').addClass('fa fa-times-circle remove')) + .addClass(response.Type) + .attr('data-subjectid', response.Id) + .attr('data-subjectstatus', 'new'); - list.append(li); + list.append(li); - updateNoSubjects(); + updateNoSubjects(); + }else{ + alert('That subject has already been added'); + } }else{ - alert('That subject has already been added'); + alert(response.Name + ' ['+response.Id+'] is a ' + response.Type + '. Only users and groups can be added.'); } }else{ alert('Unknown Id'); diff --git a/Disco.Web/Areas/Config/Views/AuthorizationRole/Show.generated.cs b/Disco.Web/Areas/Config/Views/AuthorizationRole/Show.generated.cs index 20d65989..3ba23aa3 100644 --- a/Disco.Web/Areas/Config/Views/AuthorizationRole/Show.generated.cs +++ b/Disco.Web/Areas/Config/Views/AuthorizationRole/Show.generated.cs @@ -500,7 +500,7 @@ WriteLiteral(">\r\n \r\n \').addClass(\'fa f" + -"a-lg\');\r\n if (response.Type === \'user" + -"\')\r\n liIcon.addClass(\'fa-user\');\r" + -"\n else\r\n " + -" liIcon.addClass(\'fa-users\');\r\n\r\n " + -" var li = $(\'
  • \')\r\n " + -" .append(liIcon)\r\n .append($(\'<" + -"span>\').text(response.Id == response.Name ? response.Id : response.Name + \' [\' " + -"+ response.Id + \']\'))\r\n .append($" + -"(\'\').addClass(\'fa fa-times-circle remove\'))\r\n " + -" .addClass(response.Type)\r\n " + -" .attr(\'data-subjectid\', response.Id)\r\n " + -" .attr(\'data-subjectstatus\', \'new\');\r\n\r\n " + -" list.append(li);\r\n\r\n " + -" updateNoSubjects(); \r\n " + -" }else{\r\n aler" + -"t(\'That subject has already been added\');\r\n " + -" }\r\n }else{\r\n " + -" alert(\'Unknown Id\');\r\n }\r\n " + -" }).fail(function(jqXHR, textStatus, errorThrown){\r\n " + -" alert(\'Error: \' + errorThrown);\r\n " + -" });\r\n }\r\n\r\n " + -" function updateNoSubjects(){\r\n if (list.find" + -"(\'li:visible\').length > 0)\r\n noSubjects.hide(" + -");\r\n else\r\n no" + -"Subjects.show();\r\n }\r\n\r\n f" + -"unction saveChanges(){\r\n var form = $(\'#Config_Au" + -"thRoles_Subjects_Update_Dialog_Form\').empty();\r\n\r\n " + -" list.find(\'li[data-subjectstatus!=\"removed\"]\').each(function(){\r\n " + -" var subjectId = $(this).attr(\'data-subjectid\');\r\n " + -" \r\n form.append($(" + -"\'\').attr({\r\n \'name\': \'Subjects\',\r\n" + -" \'type\': \'hidden\'\r\n " + -" }).val(subjectId));\r\n\r\n }).get();\r\n" + -"\r\n form.submit();\r\n\r\n " + -" dialog.dialog(\"disable\");\r\n dialog.dialog(\"op" + -"tion\", \"buttons\", null);\r\n }\r\n\r\n " + -" $(function(){\r\n $(\'#Config_AuthRoles_Subje" + -"cts_Update\').click(showDialog);\r\n });\r\n\r\n " + -" })();\r\n \r\n \r\n " + -" \r\n \r\n \r\n \').addClass(\'fa fa-lg\');\r\n if (" + +"response.Type === \'user\')\r\n l" + +"iIcon.addClass(\'fa-user\');\r\n else" + +"\r\n liIcon.addClass(\'fa-users\'" + +");\r\n\r\n var li = $(\'
  • \')\r\n " + +" .append(liIcon)\r\n " + +" .append($(\'\').text(response.Id == respo" + +"nse.Name ? response.Id : response.Name + \' [\' + response.Id + \']\'))\r\n " + +" .append($(\'\').addClass(\'fa fa-times" + +"-circle remove\'))\r\n .addClass" + +"(response.Type)\r\n .attr(\'data" + +"-subjectid\', response.Id)\r\n ." + +"attr(\'data-subjectstatus\', \'new\');\r\n\r\n " + +" list.append(li);\r\n\r\n update" + +"NoSubjects(); \r\n " + +" }else{\r\n alert" + +"(\'That subject has already been added\');\r\n " + +" }\r\n }else{\r\n " + +" alert(response.Name + \' [\'+response.Id+\'] is a \' + response" + +".Type + \'. Only users and groups can be added.\');\r\n " + +" }\r\n }else{\r\n " + +" alert(\'Unknown Id\');\r\n }\r\n " + +" }).fail(function(jqXHR, textStatus, errorThrown){" + +"\r\n alert(\'Error: \' + errorThrown);\r\n " + +" });\r\n }\r\n\r\n " + +" function updateNoSubjects(){\r\n if (l" + +"ist.find(\'li:visible\').length > 0)\r\n noSubjec" + +"ts.hide();\r\n else\r\n " + +" noSubjects.show();\r\n }\r\n\r\n " + +" function saveChanges(){\r\n var form = $(\'#C" + +"onfig_AuthRoles_Subjects_Update_Dialog_Form\').empty();\r\n\r\n " + +" list.find(\'li[data-subjectstatus!=\"removed\"]\').each(function(){\r\n " + +" var subjectId = $(this).attr(\'data-subjectid\');\r\n" + +" \r\n form.a" + +"ppend($(\'\').attr({\r\n \'name\': \'Subj" + +"ects\',\r\n \'type\': \'hidden\'\r\n " + +" }).val(subjectId));\r\n\r\n })." + +"get();\r\n\r\n form.submit();\r\n\r\n " + +" dialog.dialog(\"disable\");\r\n dialog.di" + +"alog(\"option\", \"buttons\", null);\r\n }\r\n\r\n " + +" $(function(){\r\n $(\'#Config_AuthRol" + +"es_Subjects_Update\').click(showDialog);\r\n });\r\n\r\n " + +" })();\r\n \r\n " + +"\r\n \r\n \r\n \r\n Save Changes"); - #line 247 "..\..\Areas\Config\Views\AuthorizationRole\Show.cshtml" + #line 251 "..\..\Areas\Config\Views\AuthorizationRole\Show.cshtml" Write(AjaxHelpers.AjaxLoader()); @@ -623,7 +628,7 @@ WriteLiteral("\r\n \r\n \r\n"); - #line 659 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 650 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" } else { @@ -1820,59 +1782,21 @@ WriteLiteral(" id=\"displayOrganisationalUnit\""); WriteLiteral(" class=\"code\""); -WriteLiteral(">\r\n"); +WriteLiteral(">\r\n \r\n"); + +WriteLiteral(" "); - #line 663 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" - - - #line default - #line hidden - - #line 663 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" - if (string.IsNullOrEmpty(Model.DeviceProfile.OrganisationalUnit)) - { + #line 655 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + Write(Model.FriendlyOrganisationalUnitName); #line default #line hidden -WriteLiteral(" {Default Computers Container}\r\n"); +WriteLiteral("\r\n \r\n \r\n"); - #line 666 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" - } - else - { - var domain = Disco.Services.Interop.ActiveDirectory.ActiveDirectory.GetDomainByDistinguishedName(Model.DeviceProfile.OrganisationalUnit); - - - - #line default - #line hidden -WriteLiteral(" \r\n"); - -WriteLiteral(" "); - - - #line 672 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" - Write(Disco.Services.Interop.ActiveDirectory.ActiveDirectoryExtensions.GetFriendlyOrganisationalUnitName(domain, Model.DeviceProfile.OrganisationalUnit)); - - - #line default - #line hidden -WriteLiteral("\r\n \r\n"); - - - #line 674 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" - } - - - #line default - #line hidden -WriteLiteral(" \r\n"); - - - #line 676 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 658 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" } @@ -1885,13 +1809,13 @@ WriteLiteral(" style=\"margin-top: 8px;\""); WriteLiteral(">\r\n"); - #line 678 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 660 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" #line default #line hidden - #line 678 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 660 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" if (canConfig) { @@ -1907,7 +1831,7 @@ WriteLiteral(" type=\"checkbox\""); WriteLiteral(" "); - #line 680 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 662 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" Write(Model.DeviceProfile.EnforceOrganisationalUnit ? new MvcHtmlString("checked=\"checked\" ") : new MvcHtmlString(string.Empty)); @@ -1928,7 +1852,7 @@ WriteLiteral(@"> $.getJSON('"); - #line 687 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 669 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" Write(Url.Action(MVC.API.DeviceProfile.UpdateEnforceOrganisationalUnit(Model.DeviceProfile.Id))); @@ -1948,7 +1872,7 @@ WriteLiteral(@"', data, function (response, result) { "); - #line 698 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 680 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" } else { @@ -1965,7 +1889,7 @@ WriteLiteral(" type=\"checkbox\""); WriteLiteral(" "); - #line 701 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 683 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" Write(Model.DeviceProfile.EnforceOrganisationalUnit ? new MvcHtmlString("checked=\"checked\" ") : new MvcHtmlString(string.Empty)); @@ -1974,7 +1898,7 @@ WriteLiteral(" "); WriteLiteral(" disabled=\"disabled\" />\r\n"); - #line 702 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 684 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" } @@ -1990,7 +1914,7 @@ WriteLiteral(">\r\n Enforce Organisational Unit\r\n WriteLiteral(" "); - #line 706 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 688 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" Write(AjaxHelpers.AjaxLoader()); @@ -2000,7 +1924,7 @@ WriteLiteral("\r\n \r\n \r\n \r "\n"); - #line 712 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 694 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" if (canDelete) { @@ -2055,7 +1979,7 @@ WriteLiteral(@"> "); - #line 748 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 730 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" } @@ -2068,13 +1992,13 @@ WriteLiteral(" class=\"actionBar\""); WriteLiteral(">\r\n"); - #line 750 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 732 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" #line default #line hidden - #line 750 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 732 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" if (canDelete) { @@ -2082,14 +2006,14 @@ WriteLiteral(">\r\n"); #line default #line hidden - #line 752 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 734 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" Write(Html.ActionLinkButton("Delete", MVC.API.DeviceProfile.Delete(Model.DeviceProfile.Id, true), "buttonDelete")); #line default #line hidden - #line 752 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 734 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" } @@ -2099,7 +2023,7 @@ WriteLiteral(">\r\n"); WriteLiteral(" "); - #line 754 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 736 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" if (Authorization.Has(Claims.Device.Actions.Export)) { @@ -2107,14 +2031,14 @@ WriteLiteral(" "); #line default #line hidden - #line 756 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 738 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" Write(Html.ActionLinkButton("Export Devices", MVC.API.DeviceProfile.ExportDevices(Model.DeviceProfile.Id))); #line default #line hidden - #line 756 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 738 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" } @@ -2124,7 +2048,7 @@ WriteLiteral(" "); WriteLiteral(" "); - #line 758 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 740 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" if (Authorization.Has(Claims.Device.Search)) { @@ -2132,14 +2056,14 @@ WriteLiteral(" "); #line default #line hidden - #line 760 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 742 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" Write(Html.ActionLinkButton("View Devices", MVC.Search.Query(Model.DeviceProfile.Id.ToString(), "DeviceProfile"))); #line default #line hidden - #line 760 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" + #line 742 "..\..\Areas\Config\Views\DeviceProfile\Show.cshtml" } diff --git a/Disco.Web/Areas/Config/Views/JobQueue/Show.cshtml b/Disco.Web/Areas/Config/Views/JobQueue/Show.cshtml index b69f1e23..1e347a9c 100644 --- a/Disco.Web/Areas/Config/Views/JobQueue/Show.cshtml +++ b/Disco.Web/Areas/Config/Views/JobQueue/Show.cshtml @@ -369,7 +369,7 @@ textAdd.watermark('Search Subjects') .autocomplete({ - source: '@(Url.Action(MVC.API.JobQueue.SearchSubjects()))', + source: '@(Url.Action(MVC.API.System.SearchSubjects()))', minLength: 2, focus: function (e, ui) { textAdd.val(ui.item.Id); @@ -428,32 +428,36 @@ var id = textAdd.val(); $.ajax({ - url: '@Url.Action(MVC.API.JobQueue.Subject())', + url: '@Url.Action(MVC.API.System.Subject())', method: 'post', data: { Id: id } }).done(function (response) { if (response) { - if (list.find('li[data-subjectid="' + response.Id + '"]').filter('[data-status!="removed"]').length == 0) { + if (response.IsGroup || response.IsUserAccount) { + if (list.find('li[data-subjectid="' + response.Id + '"]').filter('[data-status!="removed"]').length == 0) { - var liIcon = $('').addClass('fa fa-lg'); - if (response.Type === 'user') - liIcon.addClass('fa-user'); - else - liIcon.addClass('fa-users'); + var liIcon = $('').addClass('fa fa-lg'); + if (response.Type === 'user') + liIcon.addClass('fa-user'); + else + liIcon.addClass('fa-users'); - var li = $('
  • ') - .append(liIcon) - .append($('').text(response.Id == response.Name ? response.Id : response.Name + ' [' + response.Id + ']')) - .append($('').addClass('fa fa-times-circle remove')) - .addClass(response.Type) - .attr('data-subjectid', response.Id) - .attr('data-subjectstatus', 'new'); + var li = $('
  • ') + .append(liIcon) + .append($('').text(response.Id == response.Name ? response.Id : response.Name + ' [' + response.Id + ']')) + .append($('').addClass('fa fa-times-circle remove')) + .addClass(response.Type) + .attr('data-subjectid', response.Id) + .attr('data-subjectstatus', 'new'); - list.append(li); + list.append(li); - updateNoSubjects(); + updateNoSubjects(); + } else { + alert('That subject has already been added'); + } } else { - alert('That subject has already been added'); + alert(response.Name + ' [' + response.Id + '] is a ' + response.Type + '. Only users and groups can be added.'); } } else { alert('Unknown Id'); diff --git a/Disco.Web/Areas/Config/Views/JobQueue/Show.generated.cs b/Disco.Web/Areas/Config/Views/JobQueue/Show.generated.cs index dc43a109..75c9f403 100644 --- a/Disco.Web/Areas/Config/Views/JobQueue/Show.generated.cs +++ b/Disco.Web/Areas/Config/Views/JobQueue/Show.generated.cs @@ -1321,7 +1321,7 @@ WriteLiteral(">\r\n \r\n #line 372 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" - Write(Url.Action(MVC.API.JobQueue.SearchSubjects())); + Write(Url.Action(MVC.API.System.SearchSubjects())); #line default @@ -1368,7 +1368,7 @@ WriteLiteral("\',\r\n minLength: #line 431 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" - Write(Url.Action(MVC.API.JobQueue.Subject())); + Write(Url.Action(MVC.API.System.Subject())); #line default @@ -1376,53 +1376,58 @@ WriteLiteral("\',\r\n minLength: WriteLiteral("\',\r\n method: \'post\',\r\n " + " data: { Id: id }\r\n }).don" + "e(function (response) {\r\n if (response) {" + -"\r\n if (list.find(\'li[data-subjectid=\"" + -"\' + response.Id + \'\"]\').filter(\'[data-status!=\"removed\"]\').length == 0) {\r\n\r\n " + -" var liIcon = $(\'\').addClass(\'fa " + -"fa-lg\');\r\n if (response.Type === " + -"\'user\')\r\n liIcon.addClass(\'fa" + -"-user\');\r\n else\r\n " + -" liIcon.addClass(\'fa-users\');\r\n\r\n " + -" var li = $(\'
  • \')\r\n " + -" .append(liIcon)\r\n " + -" .append($(\'\').text(response.Id == response.Name ? respons" + -"e.Id : response.Name + \' [\' + response.Id + \']\'))\r\n " + -" .append($(\'\').addClass(\'fa fa-times-circle remove\'))\r\n" + -" .addClass(response.Type)\r\n " + -" .attr(\'data-subjectid\', respon" + -"se.Id)\r\n .attr(\'data-subjects" + -"tatus\', \'new\');\r\n\r\n list.append(l" + -"i);\r\n\r\n updateNoSubjects();\r\n " + -" } else {\r\n " + -" alert(\'That subject has already been added\');\r\n " + -" }\r\n } else {" + -"\r\n alert(\'Unknown Id\');\r\n " + -" }\r\n }).fail(funct" + -"ion (jqXHR, textStatus, errorThrown) {\r\n " + -"alert(\'Error: \' + errorThrown);\r\n });\r\n " + -" }\r\n\r\n function upda" + -"teNoSubjects() {\r\n if (list.find(\'li:visible\'" + -").length > 0)\r\n noSubjects.hide();\r\n " + -" else\r\n noS" + -"ubjects.show();\r\n }\r\n\r\n " + -" function saveChanges() {\r\n var form = $" + -"(\'#Config_JobQueues_Subjects_Update_Dialog_Form\').empty();\r\n\r\n " + -" list.find(\'li[data-subjectstatus!=\"removed\"]\').each(function (" + -") {\r\n var subjectId = $(this).attr(\'data-" + -"subjectid\');\r\n\r\n form.append($(\'\')" + -".attr({\r\n \'name\': \'Subjects\',\r\n " + -" \'type\': \'hidden\'\r\n " + -" }).val(subjectId));\r\n\r\n }).ge" + -"t();\r\n\r\n form.submit();\r\n\r\n " + -" dialog.dialog(\"disable\");\r\n " + -" dialog.dialog(\"option\", \"buttons\", null);\r\n }\r\n\r" + -"\n $(function () {\r\n " + -" $(\'#Config_JobQueues_Subjects_Update\').click(showDialog);\r\n " + -" });\r\n\r\n })();\r\n " + -" \r\n \r\n"); +"\r\n if (response.IsGroup || response.I" + +"sUserAccount) {\r\n if (list.find(\'" + +"li[data-subjectid=\"\' + response.Id + \'\"]\').filter(\'[data-status!=\"removed\"]\').le" + +"ngth == 0) {\r\n\r\n var liIcon =" + +" $(\'\').addClass(\'fa fa-lg\');\r\n " + +" if (response.Type === \'user\')\r\n " + +" liIcon.addClass(\'fa-user\');\r\n " + +" else\r\n liIco" + +"n.addClass(\'fa-users\');\r\n\r\n v" + +"ar li = $(\'
  • \')\r\n .appe" + +"nd(liIcon)\r\n .append($(\'<" + +"span>\').text(response.Id == response.Name ? response.Id : response.Name + \' [\' +" + +" response.Id + \']\'))\r\n .a" + +"ppend($(\'\').addClass(\'fa fa-times-circle remove\'))\r\n " + +" .addClass(response.Type)\r\n " + +" .attr(\'data-subjectid\', response.Id)\r\n " + +" .attr(\'data-subjectstatus\', \'new\'" + +");\r\n\r\n list.append(li);\r\n\r\n " + +" updateNoSubjects();\r\n " + +" } else {\r\n " + +" alert(\'That subject has already been added\');\r\n " + +" }\r\n " + +" } else {\r\n alert(response.Name" + +" + \' [\' + response.Id + \'] is a \' + response.Type + \'. Only users and groups can" + +" be added.\');\r\n }\r\n " + +" } else {\r\n aler" + +"t(\'Unknown Id\');\r\n }\r\n " + +" }).fail(function (jqXHR, textStatus, errorThrown) {\r\n " + +" alert(\'Error: \' + errorThrown);\r\n " + +" });\r\n }\r\n\r\n " + +" function updateNoSubjects() {\r\n " + +" if (list.find(\'li:visible\').length > 0)\r\n " + +" noSubjects.hide();\r\n else\r\n " + +" noSubjects.show();\r\n }" + +"\r\n\r\n function saveChanges() {\r\n " + +" var form = $(\'#Config_JobQueues_Subjects_Update_Dialog_Form\')." + +"empty();\r\n\r\n list.find(\'li[data-subjectstatus" + +"!=\"removed\"]\').each(function () {\r\n var s" + +"ubjectId = $(this).attr(\'data-subjectid\');\r\n\r\n " + +" form.append($(\'\').attr({\r\n " + +" \'name\': \'Subjects\',\r\n \'type\': \'hid" + +"den\'\r\n }).val(subjectId));\r\n\r\n " + +" }).get();\r\n\r\n form.s" + +"ubmit();\r\n\r\n dialog.dialog(\"disable\");\r\n " + +" dialog.dialog(\"option\", \"buttons\", null);\r\n " + +" }\r\n\r\n $(function () {\r\n" + +" $(\'#Config_JobQueues_Subjects_Update\').click" + +"(showDialog);\r\n });\r\n\r\n " + +" })();\r\n \r\n \r\n"); - #line 499 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 503 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" } @@ -1432,13 +1437,13 @@ WriteLiteral(" \r\n \r\n \r\n " Jobs:\r\n \r\n
    \r\n"); - #line 506 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 510 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" #line default #line hidden - #line 506 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 510 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" if (Model.Token.JobQueue.JobSubTypes.Count > 0) { @@ -1448,13 +1453,13 @@ WriteLiteral(" \r\n \r\n \r\n WriteLiteral("
      \r\n"); - #line 509 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 513 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" #line default #line hidden - #line 509 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 513 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" foreach (var jobType in Model.Token.JobQueue.JobSubTypes.GroupBy(jst => jst.JobType).OrderBy(jtg => jtg.Key.Description)) { @@ -1466,7 +1471,7 @@ WriteLiteral("
    • \r\n"); WriteLiteral(" "); - #line 512 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 516 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" Write(jobType.Key.Description); @@ -1475,13 +1480,13 @@ WriteLiteral(" "); WriteLiteral("\r\n
        \r\n"); - #line 514 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 518 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" #line default #line hidden - #line 514 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 518 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" if (jobType.Count() == Model.JobTypes.FirstOrDefault(jt => jt.Id == jobType.Key.Id).JobSubTypes.Count) { @@ -1495,7 +1500,7 @@ WriteLiteral(" class=\"smallMessage\""); WriteLiteral(">[All Sub Types]\r\n"); - #line 517 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 521 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" } else { @@ -1508,7 +1513,7 @@ WriteLiteral(">[All Sub Types]\r\n"); WriteLiteral("
      • "); - #line 522 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 526 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" Write(jobSubType.Description); @@ -1517,7 +1522,7 @@ WriteLiteral("
      • "); WriteLiteral("
      • \r\n"); - #line 523 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 527 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" } } @@ -1528,7 +1533,7 @@ WriteLiteral("
      \r\n "\n"); - #line 527 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 531 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" } @@ -1537,7 +1542,7 @@ WriteLiteral("
    \r\n WriteLiteral(" \r\n"); - #line 529 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 533 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" } else { @@ -1552,7 +1557,7 @@ WriteLiteral("<None>"); WriteLiteral("\r\n"); - #line 533 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 537 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" } @@ -1561,13 +1566,13 @@ WriteLiteral("\r\n"); WriteLiteral("
    \r\n"); - #line 535 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 539 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" #line default #line hidden - #line 535 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 539 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" if (canConfig) { @@ -1595,13 +1600,13 @@ WriteLiteral(" title=\"Job Queue Automatic Types\""); WriteLiteral(">\r\n"); - #line 539 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 543 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" #line default #line hidden - #line 539 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 543 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" using (Html.BeginForm(MVC.API.JobQueue.UpdateJobSubTypes(Model.Token.JobQueue.Id, null, true))) { var selectedTypes = Model.Token.JobQueue.JobSubTypes.Select(jst => jst.JobType).Distinct().ToList(); @@ -1613,15 +1618,15 @@ WriteLiteral(">\r\n"); #line hidden WriteLiteral(" (jt.Id + #line 548 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" +, Tuple.Create(Tuple.Create("", 28967), Tuple.Create(jt.Id #line default #line hidden -, 28541), false) +, 28967), false) ); WriteLiteral(" class=\"jobTypes\""); @@ -1629,35 +1634,35 @@ WriteLiteral(" class=\"jobTypes\""); WriteLiteral(">\r\n

    \r\n (jt.Id + #line 550 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" +, Tuple.Create(Tuple.Create("", 29087), Tuple.Create(jt.Id #line default #line hidden -, 28661), false) +, 29087), false) ); WriteLiteral(" class=\"jobType\""); WriteLiteral(" type=\"checkbox\""); -WriteAttribute("value", Tuple.Create(" value=\"", 28702), Tuple.Create("\"", 28718) +WriteAttribute("value", Tuple.Create(" value=\"", 29128), Tuple.Create("\"", 29144) - #line 546 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" - , Tuple.Create(Tuple.Create("", 28710), Tuple.Create(jt.Id + #line 550 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + , Tuple.Create(Tuple.Create("", 29136), Tuple.Create(jt.Id #line default #line hidden -, 28710), false) +, 29136), false) ); WriteLiteral(" "); - #line 546 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 550 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" Write(selectedTypes.Contains(jt) ? "checked=\"checked\"" : null); @@ -1665,21 +1670,21 @@ WriteLiteral(" "); #line hidden WriteLiteral(" />(jt.Id + #line 550 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + , Tuple.Create(Tuple.Create("", 29227), Tuple.Create(jt.Id #line default #line hidden -, 28801), false) +, 29227), false) ); WriteLiteral(">"); - #line 546 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 550 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" Write(jt.Description); @@ -1687,15 +1692,15 @@ WriteLiteral(">"); #line hidden WriteLiteral("

    \r\n (jt.Id + #line 551 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" +, Tuple.Create(Tuple.Create("", 29317), Tuple.Create(jt.Id #line default #line hidden -, 28891), false) +, 29317), false) ); WriteLiteral(" class=\"jobSubTypes\""); @@ -1705,7 +1710,7 @@ WriteLiteral(">\r\n"); WriteLiteral(" "); - #line 548 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 552 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" Write(CommonHelpers.CheckboxBulkSelect(string.Format("CheckboxBulkSelect_{0}", jt.Id), "div")); @@ -1716,7 +1721,7 @@ WriteLiteral("\r\n"); WriteLiteral(" "); - #line 549 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 553 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" Write(CommonHelpers.CheckBoxList("JobSubTypes", jt.JobSubTypes.OrderBy(jst => jst.Description).ToSelectListItems(Model.Token.JobQueue.JobSubTypes), 2)); @@ -1726,7 +1731,7 @@ WriteLiteral("\r\n \r\n ""); - #line 552 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 556 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" } } @@ -1774,7 +1779,7 @@ WriteLiteral(" \r\n"); - #line 617 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 621 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" } @@ -1795,7 +1800,7 @@ WriteLiteral(">
     When jobs of these types are created, they will automat " \r\n\r\n"); - #line 625 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 629 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" if (canDelete || canShowJobs) { @@ -1811,7 +1816,7 @@ WriteLiteral(">\r\n"); WriteLiteral(" "); - #line 628 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 632 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" Write(Html.ActionLinkButton("Delete", MVC.API.JobQueue.Delete(Model.Token.JobQueue.Id, true), "Config_JobQueues_Actions_Delete_Button")); @@ -1866,7 +1871,7 @@ WriteLiteral(@"> WriteLiteral(" "); - #line 665 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 669 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" Write(Html.ActionLinkButton(string.Format("Show {0} job{1}", Model.OpenJobCount, (Model.OpenJobCount == 1 ? null : "s")), MVC.Job.Queue(Model.Token.JobQueue.Id), "Config_JobQueues_Actions_ShowJobs_Button")); @@ -1875,7 +1880,7 @@ WriteLiteral(" "); WriteLiteral("\r\n \r\n"); - #line 667 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" + #line 671 "..\..\Areas\Config\Views\JobQueue\Show.cshtml" } #line default diff --git a/Disco.Web/Areas/Config/Views/SystemConfig/Index.cshtml b/Disco.Web/Areas/Config/Views/SystemConfig/Index.cshtml index 46eae4ba..0a163011 100644 --- a/Disco.Web/Areas/Config/Views/SystemConfig/Index.cshtml +++ b/Disco.Web/Areas/Config/Views/SystemConfig/Index.cshtml @@ -148,22 +148,23 @@ Primary Domain: - @Model.ADPrimaryDomain.DnsName [@Model.ADPrimaryDomain.NetBiosName] + @Model.ADPrimaryDomain.Name [@Model.ADPrimaryDomain.NetBiosName] Additional Domains: - @if (Model.ADAdditionalDomains.Count > 0) + @if (Model.ADDomains.Count > 1) { - var adDomainFirst = Model.ADAdditionalDomains.First(); - @adDomainFirst.DnsName [@adDomainFirst.NetBiosName] - foreach (var adDomain in Model.ADAdditionalDomains.Skip(1)) + var adAdditionalDomains = Model.ADDomains.Where(d => d != Model.ADPrimaryDomain).OrderBy(d => d.Name).ToList(); + var adDomainFirst = adAdditionalDomains.First(); + @adDomainFirst.Name [@adDomainFirst.NetBiosName] + foreach (var adDomain in adAdditionalDomains.Skip(1)) {
    - @adDomain.DnsName [@adDomain.NetBiosName] + @adDomain.Name [@adDomain.NetBiosName]
    } } @@ -178,18 +179,42 @@ @Model.ADSite.Name -
    + + + + Servers: + +
    - @if (Model.ADSiteServers.Count > 0) + @if (Model.ADServers.Count > 0) { - Servers:
      - @foreach (var siteServer in Model.ADSiteServers) + @foreach (var server in Model.ADServers) { - var server = siteServer.Item1; - var reachable = siteServer.Item2; + var serverDescription = string.Format("{0} [{1}]", server.Name.EndsWith(server.Domain.Name, StringComparison.OrdinalIgnoreCase) ? server.Name.Substring(0, server.Name.Length - server.Domain.Name.Length - 1) : server.Name, server.Domain.NetBiosName); + var reachable = server.IsAvailable;
    • -  @(server.Name) + @if (server.IsAvailable) + { + + } + else + { + + } + @(serverDescription) + @if (server.IsSiteServer) + { + + } + else + { + + } + @if (server.IsWritable) + { + + }
    • }
    @@ -210,11 +235,12 @@ @if (Model.ADForestServers == null) {
    - @Html.CheckBoxFor(m => m.ADSearchEntireForest, new { disabled = "disabled" }) @Html.LabelFor(m => m.ADSearchEntireForest) + @Html.CheckBoxFor(m => m.ADSearchAllForestServers, new { disabled = "disabled" }) @Html.LabelFor(m => m.ADSearchAllForestServers)
     Forest servers are currently being retrieved. -
    Try refreshing this page in a moment. +
    + Try refreshing this page in a moment.
    } else @@ -225,20 +251,20 @@
    @if (!canSearchEntireForest) { - @Html.CheckBoxFor(m => m.ADSearchEntireForest, new { disabled = "disabled" }) @Html.LabelFor(m => m.ADSearchEntireForest) + @Html.CheckBoxFor(m => m.ADSearchAllForestServers, new { disabled = "disabled" }) @Html.LabelFor(m => m.ADSearchAllForestServers)
     Disco will not search entire forests which consist of more than @(Disco.Services.Interop.ActiveDirectory.ActiveDirectory.MaxForestServerSearch) servers. Only servers within this site will be searched.
    } else { - @Html.CheckBoxFor(m => m.ADSearchEntireForest) @Html.LabelFor(m => m.ADSearchEntireForest) @AjaxHelpers.AjaxLoader() + @Html.CheckBoxFor(m => m.ADSearchAllForestServers) @Html.LabelFor(m => m.ADSearchAllForestServers) @AjaxHelpers.AjaxLoader()
    - If this setting is enabled, Disco will search all servers within the forest rather than only servers within this site. + If this setting is enabled, Disco will query all servers within the forest rather than only servers within this site.
    } @@ -247,7 +273,7 @@ else {
    - @Html.CheckBoxFor(m => m.ADSearchEntireForest, new { disabled = "disabled" }) @Html.LabelFor(m => m.ADSearchEntireForest) + @Html.CheckBoxFor(m => m.ADSearchAllForestServers, new { disabled = "disabled" }) @Html.LabelFor(m => m.ADSearchAllForestServers)
    If this setting is enabled, Disco will search all servers within the forest rather than only servers within this site.
    @@ -255,11 +281,25 @@ }

    - Servers: -
      - @foreach (var server in Model.ADForestServers.OrderBy(s => s)) + All Servers: +
        + @{ + var domainIndex = Model.ADDomains.ToDictionary(d => d.Name, StringComparer.OrdinalIgnoreCase); + foreach (var server in Model.ADForestServers.OrderBy(s => s)) + { + var isSiteServer = Model.ADServers.Any(s => s.IsSiteServer && s.Name.Equals(server, StringComparison.OrdinalIgnoreCase)); + var serverDescription = server; + if (server.Contains('.')) + { + Disco.Services.Interop.ActiveDirectory.ADDomain serverDomain; + if (domainIndex.TryGetValue(server.Substring(server.IndexOf('.') + 1), out serverDomain)) { -
      • @server @(Model.ADSiteServers.Count(ss => ss.Item1.Name.Equals(server, StringComparison.InvariantCultureIgnoreCase)) > 0 ? "[Site Server]" : null)
      • + serverDescription = string.Format("{0} [{1}]", server.Substring(0, server.IndexOf('.')), serverDomain.NetBiosName); + } + } +
      • @serverDescription@if (isSiteServer) + { }
      • + } }
      \r\n"); +WriteLiteral("\', \'SearchAllForestServers\');\r\n });\r\n " + +" \r\n"); - #line 244 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 270 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" } @@ -948,7 +1061,7 @@ WriteLiteral("\', \'SearchEntireForest\');\r\n }) WriteLiteral("
    \r\n"); - #line 246 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 272 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" } else { @@ -961,8 +1074,8 @@ WriteLiteral("
    \r\n"); WriteLiteral(" "); - #line 250 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" - Write(Html.CheckBoxFor(m => m.ADSearchEntireForest, new { disabled = "disabled" })); + #line 276 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + Write(Html.CheckBoxFor(m => m.ADSearchAllForestServers, new { disabled = "disabled" })); #line default @@ -970,8 +1083,8 @@ WriteLiteral(" "); WriteLiteral(" "); - #line 250 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" - Write(Html.LabelFor(m => m.ADSearchEntireForest)); + #line 276 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + Write(Html.LabelFor(m => m.ADSearchAllForestServers)); #line default @@ -985,29 +1098,43 @@ WriteLiteral(">\r\n If this setting is enabled, Disco "
    \r\n
    \r\n"); - #line 255 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 281 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" } #line default #line hidden WriteLiteral("
    \r\n
    \r\n " + -" Servers:\r\n All Servers:\r\n \r\n"); - #line 260 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 286 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" #line default #line hidden - #line 260 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" - foreach (var server in Model.ADForestServers.OrderBy(s => s)) + #line 286 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + + var domainIndex = Model.ADDomains.ToDictionary(d => d.Name, StringComparer.OrdinalIgnoreCase); + foreach (var server in Model.ADForestServers.OrderBy(s => s)) + { + var isSiteServer = Model.ADServers.Any(s => s.IsSiteServer && s.Name.Equals(server, StringComparison.OrdinalIgnoreCase)); + var serverDescription = server; + if (server.Contains('.')) + { + Disco.Services.Interop.ActiveDirectory.ADDomain serverDomain; + if (domainIndex.TryGetValue(server.Substring(server.IndexOf('.') + 1), out serverDomain)) { + serverDescription = string.Format("{0} [{1}]", server.Substring(0, server.IndexOf('.')), serverDomain.NetBiosName); + } + } #line default @@ -1015,31 +1142,46 @@ WriteLiteral(">\r\n"); WriteLiteral("
  • "); - #line 262 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" - Write(server); + #line 300 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + Write(serverDescription); #line default #line hidden -WriteLiteral(" "); +WriteLiteral(""); - #line 262 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" - Write(Model.ADSiteServers.Count(ss => ss.Item1.Name.Equals(server, StringComparison.InvariantCultureIgnoreCase)) > 0 ? "[Site Server]" : null); + #line 300 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + if (isSiteServer) + { + + #line default + #line hidden +WriteLiteral(" "); + + + #line 301 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + } #line default #line hidden WriteLiteral("
  • \r\n"); - #line 263 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" - } - + #line 302 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + } + #line default #line hidden -WriteLiteral(@" +WriteLiteral(@" + \r\n"); - #line 433 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 473 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" } @@ -1335,7 +1477,7 @@ WriteLiteral("\', null, function (data) {\r\n WriteLiteral(" \r\n \r\n \r\n\r\n"); - #line 438 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 478 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" if (canConfigProxy) { using (Html.BeginForm(MVC.API.System.UpdateProxySettings())) @@ -1360,7 +1502,7 @@ WriteLiteral(">Address:\r\n \r\n \r\n"); WriteLiteral(" "); - #line 449 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 489 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" Write(Html.EditorFor(m => m.ProxyAddress)); @@ -1371,7 +1513,7 @@ WriteLiteral("
    \r\n"); WriteLiteral(" "); - #line 450 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 490 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" Write(Html.ValidationMessageFor(m => m.ProxyAddress)); @@ -1387,7 +1529,7 @@ WriteLiteral(">Port:\r\n \r\n \r\n"); WriteLiteral(" "); - #line 457 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 497 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" Write(Html.EditorFor(m => m.ProxyPort)); @@ -1398,7 +1540,7 @@ WriteLiteral("
    \r\n"); WriteLiteral(" "); - #line 458 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 498 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" Write(Html.ValidationMessageFor(m => m.ProxyPort)); @@ -1414,7 +1556,7 @@ WriteLiteral(">Username:\r\n \r\n \r\n"); WriteLiteral(" "); - #line 465 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 505 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" Write(Html.EditorFor(m => m.ProxyUsername)); @@ -1425,7 +1567,7 @@ WriteLiteral("
    \r\n"); WriteLiteral(" "); - #line 466 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 506 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" Write(Html.ValidationMessageFor(m => m.ProxyUsername)); @@ -1441,7 +1583,7 @@ WriteLiteral(">Password:\r\n \r\n \r\n"); WriteLiteral(" "); - #line 473 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 513 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" Write(Html.EditorFor(m => m.ProxyPassword)); @@ -1452,7 +1594,7 @@ WriteLiteral("
    \r\n"); WriteLiteral(" "); - #line 474 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 514 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" Write(Html.ValidationMessageFor(m => m.ProxyPassword)); @@ -1474,7 +1616,7 @@ WriteLiteral(" value=\"Save Proxy Settings\""); WriteLiteral(" />\r\n \r\n \r\n \r\n \r\n"); - #line 486 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 526 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" } } else @@ -1499,7 +1641,7 @@ WriteLiteral(">Address:\r\n \r\n \r\n"); WriteLiteral(" "); - #line 497 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 537 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" Write(Html.DisplayFor(m => m.ProxyAddress)); @@ -1515,7 +1657,7 @@ WriteLiteral(">Port:\r\n \r\n \r\n"); WriteLiteral(" "); - #line 504 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 544 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" Write(Html.DisplayFor(m => m.ProxyPort)); @@ -1531,7 +1673,7 @@ WriteLiteral(">Username:\r\n \r\n \r\n"); WriteLiteral(" "); - #line 511 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 551 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" Write(Html.DisplayFor(m => m.ProxyUsername)); @@ -1546,7 +1688,7 @@ WriteLiteral(">Password:\r\n \r\n ******* "\r\n \r\n \r\n \r\n"); - #line 522 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 562 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" } @@ -1561,7 +1703,7 @@ WriteLiteral(">\r\n"); WriteLiteral(" "); - #line 524 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" + #line 564 "..\..\Areas\Config\Views\SystemConfig\Index.cshtml" Write(Html.ActionLinkButton("Update Device Last Network Logons", MVC.API.System.UpdateLastNetworkLogonDates())); diff --git a/Disco.Web/ClientSource/Style/Images/AttachmentTypes/MimeTypeIcons.cs b/Disco.Web/ClientSource/Style/Images/AttachmentTypes/MimeTypeIcons.cs index 73d02781..5444d057 100644 --- a/Disco.Web/ClientSource/Style/Images/AttachmentTypes/MimeTypeIcons.cs +++ b/Disco.Web/ClientSource/Style/Images/AttachmentTypes/MimeTypeIcons.cs @@ -22,7 +22,7 @@ namespace Disco.Web.ClientSource.Style.Images.AttachmentTypes } // Generic 'image' icon - if (MimeType.StartsWith("image/", StringComparison.InvariantCultureIgnoreCase)) + if (MimeType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) return Links.ClientSource.Style.Images.AttachmentTypes.image_png; // All other Attachments diff --git a/Disco.Web/Controllers/InitialConfigController.cs b/Disco.Web/Controllers/InitialConfigController.cs index 1da37b5b..25264acf 100644 --- a/Disco.Web/Controllers/InitialConfigController.cs +++ b/Disco.Web/Controllers/InitialConfigController.cs @@ -12,8 +12,8 @@ using System.Management; using System.Web; using Disco.Services.Users; using Disco.Services.Interop.ActiveDirectory; -using Disco.Models.Interop.ActiveDirectory; using Disco.Services.Authorization; +using Disco.Web.Areas.API.Models.Shared; namespace Disco.Web.Controllers { @@ -255,9 +255,9 @@ namespace Disco.Web.Controllers public virtual ActionResult Administrators() { var administratorSubjects = UserService.AdministratorSubjectIds - .Select(subjectId => ActiveDirectory.RetrieveObject(subjectId, Quick: true)) + .Select(subjectId => ActiveDirectory.RetrieveADObject(subjectId, Quick: true)) .Where(item => item != null) - .Select(item => Disco.Web.Areas.Config.Models.AuthorizationRole.SubjectDescriptorModel.FromActiveDirectoryObject(item)) + .Select(item => SubjectDescriptorModel.FromActiveDirectoryObject(item)) .OrderBy(item => item.Name).ToList(); var m = new AdministratorsModel() @@ -269,11 +269,11 @@ namespace Disco.Web.Controllers } public virtual ActionResult AdministratorsSearch(string term) { - var groupResults = ActiveDirectory.SearchGroups(term).Cast(); - var userResults = ActiveDirectory.SearchUserAccounts(term).Cast(); + var groupResults = ActiveDirectory.SearchADGroups(term).Cast(); + var userResults = ActiveDirectory.SearchADUserAccounts(term, Quick: true).Cast(); var results = groupResults.Concat(userResults).OrderBy(r => r.SamAccountName) - .Select(r => Disco.Web.Areas.API.Models.AuthorizationRole.SubjectItem.FromActiveDirectoryObject(r)).ToList(); + .Select(r => SubjectDescriptorModel.FromActiveDirectoryObject(r)).ToList(); return Json(results, JsonRequestBehavior.AllowGet); } @@ -282,14 +282,14 @@ namespace Disco.Web.Controllers if (string.IsNullOrWhiteSpace(Id)) return Json(null, JsonRequestBehavior.AllowGet); else if (!Id.Contains(@"\")) - Id = string.Format(@"{0}\{1}", ActiveDirectory.PrimaryDomain.NetBiosName, Id); + Id = string.Format(@"{0}\{1}", ActiveDirectory.Context.PrimaryDomain.NetBiosName, Id); - var subject = ActiveDirectory.RetrieveObject(Id, Quick: true); + var subject = ActiveDirectory.RetrieveADObject(Id, Quick: true); - if (subject == null || !(subject is ActiveDirectoryUserAccount || subject is ActiveDirectoryGroup)) + if (subject == null || !(subject is ADUserAccount || subject is ADGroup)) return Json(null, JsonRequestBehavior.AllowGet); else - return Json(Disco.Web.Areas.API.Models.AuthorizationRole.SubjectItem.FromActiveDirectoryObject(subject), JsonRequestBehavior.AllowGet); + return Json(SubjectDescriptorModel.FromActiveDirectoryObject(subject), JsonRequestBehavior.AllowGet); } [HttpPost] public virtual ActionResult Administrators(string[] Subjects) @@ -305,14 +305,14 @@ namespace Disco.Web.Controllers var subjects = Subjects .Where(s => !string.IsNullOrWhiteSpace(s)) .Select(s => s.Trim()) - .Select(s => Tuple.Create(s, ActiveDirectory.RetrieveObject(s, Quick: true))) + .Select(s => Tuple.Create(s, ActiveDirectory.RetrieveADObject(s, Quick: true))) .ToList(); var invalidSubjects = subjects.Where(s => s.Item2 == null).ToList(); if (invalidSubjects.Count > 0) throw new ArgumentException(string.Format("Subjects not found: {0}", string.Join(", ", invalidSubjects)), "Subjects"); - proposedSubjects = subjects.Select(s => s.Item2.NetBiosId).OrderBy(s => s).ToArray(); + proposedSubjects = subjects.Select(s => s.Item2.Id).OrderBy(s => s).ToArray(); var currentSubjects = UserService.AdministratorSubjectIds; removedSubjects = currentSubjects.Except(proposedSubjects).ToArray(); addedSubjects = proposedSubjects.Except(currentSubjects).ToArray(); diff --git a/Disco.Web/Controllers/UserController.cs b/Disco.Web/Controllers/UserController.cs index 0c1b0cc0..28895afe 100644 --- a/Disco.Web/Controllers/UserController.cs +++ b/Disco.Web/Controllers/UserController.cs @@ -36,7 +36,7 @@ namespace Disco.Web.Controllers throw new ArgumentNullException("id", "The User Id must be provided"); if (string.IsNullOrEmpty(Domain)) - id = ActiveDirectory.PrimaryDomain.NetBiosName + @"\" + id; + id = ActiveDirectory.Context.PrimaryDomain.NetBiosName + @"\" + id; else id = Domain + @"\" + id; diff --git a/Disco.Web/Disco.Web.csproj b/Disco.Web/Disco.Web.csproj index cbe194e8..fa19569b 100644 --- a/Disco.Web/Disco.Web.csproj +++ b/Disco.Web/Disco.Web.csproj @@ -193,9 +193,8 @@ - - + @@ -203,7 +202,6 @@ - @@ -1998,6 +1996,8 @@ + + @@ -2053,7 +2053,7 @@ False - + diff --git a/Disco.Web/Extensions/T4MVCExtensions.cs b/Disco.Web/Extensions/T4MVCExtensions.cs index 3c006f85..8e7018c3 100644 --- a/Disco.Web/Extensions/T4MVCExtensions.cs +++ b/Disco.Web/Extensions/T4MVCExtensions.cs @@ -18,7 +18,7 @@ namespace Disco.Web.Controllers throw new ArgumentException("The User Id is not in the correct format ({Domain}\\{Id})", "id"); string userDomain; - if (splitId.Item1.Equals(ActiveDirectory.PrimaryDomain.NetBiosName, StringComparison.InvariantCultureIgnoreCase)) + if (splitId.Item1.Equals(ActiveDirectory.Context.PrimaryDomain.NetBiosName, StringComparison.OrdinalIgnoreCase)) userDomain = null; // Url doesn't contain Domain if it is the default. else userDomain = splitId.Item1; diff --git a/Disco.Web/Models/InitialConfig/AdministratorsModel.cs b/Disco.Web/Models/InitialConfig/AdministratorsModel.cs index 87e90da1..35509157 100644 --- a/Disco.Web/Models/InitialConfig/AdministratorsModel.cs +++ b/Disco.Web/Models/InitialConfig/AdministratorsModel.cs @@ -1,4 +1,5 @@ -using Disco.Web.Areas.Config.Models.AuthorizationRole; +using Disco.Web.Areas.API.Models.Shared; +using Disco.Web.Areas.Config.Models.AuthorizationRole; using System.Collections.Generic; namespace Disco.Web.Models.InitialConfig diff --git a/Disco.Web/Models/InitialConfig/DatabaseModel.cs b/Disco.Web/Models/InitialConfig/DatabaseModel.cs index f88b7e82..7fb956c5 100644 --- a/Disco.Web/Models/InitialConfig/DatabaseModel.cs +++ b/Disco.Web/Models/InitialConfig/DatabaseModel.cs @@ -55,9 +55,9 @@ namespace Disco.Web.Models.InitialConfig { DataSource = this.Server, InitialCatalog = this.DatabaseName, - IntegratedSecurity = (this.AuthMethod.Equals("SSPI", StringComparison.InvariantCultureIgnoreCase)), - UserID = (this.AuthMethod.Equals("SQL", StringComparison.InvariantCultureIgnoreCase)) ? this.Auth_SQL_Username : string.Empty, - Password = (this.AuthMethod.Equals("SQL", StringComparison.InvariantCultureIgnoreCase)) ? this.Auth_SQL_Password : string.Empty, + IntegratedSecurity = (this.AuthMethod.Equals("SSPI", StringComparison.OrdinalIgnoreCase)), + UserID = (this.AuthMethod.Equals("SQL", StringComparison.OrdinalIgnoreCase)) ? this.Auth_SQL_Username : string.Empty, + Password = (this.AuthMethod.Equals("SQL", StringComparison.OrdinalIgnoreCase)) ? this.Auth_SQL_Password : string.Empty, ApplicationName = "Disco WebApp", MultipleActiveResultSets = true, Pooling = true @@ -97,7 +97,7 @@ namespace Disco.Web.Models.InitialConfig { var instance = validationContext.ObjectInstance as DatabaseModel; - if (instance != null && instance.AuthMethod != null && instance.AuthMethod.Equals("SQL", StringComparison.InvariantCultureIgnoreCase)) + if (instance != null && instance.AuthMethod != null && instance.AuthMethod.Equals("SQL", StringComparison.OrdinalIgnoreCase)) { var stringValue = value as string; if (string.IsNullOrWhiteSpace(stringValue)) diff --git a/Disco.Web/T4MVC.cs b/Disco.Web/T4MVC.cs index fff4b91e..85f5ba77 100644 --- a/Disco.Web/T4MVC.cs +++ b/Disco.Web/T4MVC.cs @@ -2386,18 +2386,6 @@ namespace Disco.Web.Areas.API.Controllers { return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.UpdateAdministratorSubjects); } - [NonAction] - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public virtual System.Web.Mvc.ActionResult SearchSubjects() - { - return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.SearchSubjects); - } - [NonAction] - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public virtual System.Web.Mvc.ActionResult Subject() - { - return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.Subject); - } [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] public AuthorizationRoleController Actions { get { return MVC.API.AuthorizationRole; } } @@ -2420,8 +2408,6 @@ namespace Disco.Web.Areas.API.Controllers public readonly string UpdateSubjects = "UpdateSubjects"; public readonly string Delete = "Delete"; public readonly string UpdateAdministratorSubjects = "UpdateAdministratorSubjects"; - public readonly string SearchSubjects = "SearchSubjects"; - public readonly string Subject = "Subject"; } [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] @@ -2433,8 +2419,6 @@ namespace Disco.Web.Areas.API.Controllers public const string UpdateSubjects = "UpdateSubjects"; public const string Delete = "Delete"; public const string UpdateAdministratorSubjects = "UpdateAdministratorSubjects"; - public const string SearchSubjects = "SearchSubjects"; - public const string Subject = "Subject"; } @@ -2497,22 +2481,6 @@ namespace Disco.Web.Areas.API.Controllers public readonly string Subjects = "Subjects"; public readonly string redirect = "redirect"; } - static readonly ActionParamsClass_SearchSubjects s_params_SearchSubjects = new ActionParamsClass_SearchSubjects(); - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public ActionParamsClass_SearchSubjects SearchSubjectsParams { get { return s_params_SearchSubjects; } } - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public class ActionParamsClass_SearchSubjects - { - public readonly string term = "term"; - } - static readonly ActionParamsClass_Subject s_params_Subject = new ActionParamsClass_Subject(); - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public ActionParamsClass_Subject SubjectParams { get { return s_params_Subject; } } - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public class ActionParamsClass_Subject - { - public readonly string Id = "Id"; - } static readonly ViewsClass s_views = new ViewsClass(); [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] public ViewsClass Views { get { return s_views; } } @@ -2603,26 +2571,6 @@ namespace Disco.Web.Areas.API.Controllers return callInfo; } - partial void SearchSubjectsOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string term); - - public override System.Web.Mvc.ActionResult SearchSubjects(string term) - { - var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.SearchSubjects); - ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "term", term); - SearchSubjectsOverride(callInfo, term); - return callInfo; - } - - partial void SubjectOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string Id); - - public override System.Web.Mvc.ActionResult Subject(string Id) - { - var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.Subject); - ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "Id", Id); - SubjectOverride(callInfo, Id); - return callInfo; - } - } } @@ -8079,18 +8027,6 @@ namespace Disco.Web.Areas.API.Controllers { return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.Delete); } - [NonAction] - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public virtual System.Web.Mvc.ActionResult SearchSubjects() - { - return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.SearchSubjects); - } - [NonAction] - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public virtual System.Web.Mvc.ActionResult Subject() - { - return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.Subject); - } [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] public JobQueueController Actions { get { return MVC.API.JobQueue; } } @@ -8118,8 +8054,6 @@ namespace Disco.Web.Areas.API.Controllers public readonly string UpdateSubjects = "UpdateSubjects"; public readonly string UpdateJobSubTypes = "UpdateJobSubTypes"; public readonly string Delete = "Delete"; - public readonly string SearchSubjects = "SearchSubjects"; - public readonly string Subject = "Subject"; } [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] @@ -8136,8 +8070,6 @@ namespace Disco.Web.Areas.API.Controllers public const string UpdateSubjects = "UpdateSubjects"; public const string UpdateJobSubTypes = "UpdateJobSubTypes"; public const string Delete = "Delete"; - public const string SearchSubjects = "SearchSubjects"; - public const string Subject = "Subject"; } @@ -8252,22 +8184,6 @@ namespace Disco.Web.Areas.API.Controllers public readonly string id = "id"; public readonly string redirect = "redirect"; } - static readonly ActionParamsClass_SearchSubjects s_params_SearchSubjects = new ActionParamsClass_SearchSubjects(); - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public ActionParamsClass_SearchSubjects SearchSubjectsParams { get { return s_params_SearchSubjects; } } - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public class ActionParamsClass_SearchSubjects - { - public readonly string term = "term"; - } - static readonly ActionParamsClass_Subject s_params_Subject = new ActionParamsClass_Subject(); - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public ActionParamsClass_Subject SubjectParams { get { return s_params_Subject; } } - [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public class ActionParamsClass_Subject - { - public readonly string Id = "Id"; - } static readonly ViewsClass s_views = new ViewsClass(); [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] public ViewsClass Views { get { return s_views; } } @@ -8420,26 +8336,6 @@ namespace Disco.Web.Areas.API.Controllers return callInfo; } - partial void SearchSubjectsOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string term); - - public override System.Web.Mvc.ActionResult SearchSubjects(string term) - { - var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.SearchSubjects); - ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "term", term); - SearchSubjectsOverride(callInfo, term); - return callInfo; - } - - partial void SubjectOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string Id); - - public override System.Web.Mvc.ActionResult Subject(string Id) - { - var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.Subject); - ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "Id", Id); - SubjectOverride(callInfo, Id); - return callInfo; - } - } } @@ -9293,9 +9189,21 @@ namespace Disco.Web.Areas.API.Controllers } [NonAction] [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public virtual System.Web.Mvc.ActionResult UpdateActiveDirectorySearchEntireForest() + public virtual System.Web.Mvc.ActionResult UpdateActiveDirectorySearchAllForestServers() { - return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.UpdateActiveDirectorySearchEntireForest); + return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.UpdateActiveDirectorySearchAllForestServers); + } + [NonAction] + [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] + public virtual System.Web.Mvc.ActionResult SearchSubjects() + { + return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.SearchSubjects); + } + [NonAction] + [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] + public virtual System.Web.Mvc.ActionResult Subject() + { + return new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.Subject); } [NonAction] [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] @@ -9328,8 +9236,10 @@ namespace Disco.Web.Areas.API.Controllers public readonly string DeleteOrganisationAddress = "DeleteOrganisationAddress"; public readonly string UpdateMultiSiteMode = "UpdateMultiSiteMode"; public readonly string UpdateActiveDirectorySearchScope = "UpdateActiveDirectorySearchScope"; - public readonly string UpdateActiveDirectorySearchEntireForest = "UpdateActiveDirectorySearchEntireForest"; + public readonly string UpdateActiveDirectorySearchAllForestServers = "UpdateActiveDirectorySearchAllForestServers"; public readonly string DomainOrganisationalUnits = "DomainOrganisationalUnits"; + public readonly string SearchSubjects = "SearchSubjects"; + public readonly string Subject = "Subject"; public readonly string UpdateProxySettings = "UpdateProxySettings"; } @@ -9345,8 +9255,10 @@ namespace Disco.Web.Areas.API.Controllers public const string DeleteOrganisationAddress = "DeleteOrganisationAddress"; public const string UpdateMultiSiteMode = "UpdateMultiSiteMode"; public const string UpdateActiveDirectorySearchScope = "UpdateActiveDirectorySearchScope"; - public const string UpdateActiveDirectorySearchEntireForest = "UpdateActiveDirectorySearchEntireForest"; + public const string UpdateActiveDirectorySearchAllForestServers = "UpdateActiveDirectorySearchAllForestServers"; public const string DomainOrganisationalUnits = "DomainOrganisationalUnits"; + public const string SearchSubjects = "SearchSubjects"; + public const string Subject = "Subject"; public const string UpdateProxySettings = "UpdateProxySettings"; } @@ -9409,15 +9321,31 @@ namespace Disco.Web.Areas.API.Controllers public readonly string Containers = "Containers"; public readonly string redirect = "redirect"; } - static readonly ActionParamsClass_UpdateActiveDirectorySearchEntireForest s_params_UpdateActiveDirectorySearchEntireForest = new ActionParamsClass_UpdateActiveDirectorySearchEntireForest(); + static readonly ActionParamsClass_UpdateActiveDirectorySearchAllForestServers s_params_UpdateActiveDirectorySearchAllForestServers = new ActionParamsClass_UpdateActiveDirectorySearchAllForestServers(); [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public ActionParamsClass_UpdateActiveDirectorySearchEntireForest UpdateActiveDirectorySearchEntireForestParams { get { return s_params_UpdateActiveDirectorySearchEntireForest; } } + public ActionParamsClass_UpdateActiveDirectorySearchAllForestServers UpdateActiveDirectorySearchAllForestServersParams { get { return s_params_UpdateActiveDirectorySearchAllForestServers; } } [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] - public class ActionParamsClass_UpdateActiveDirectorySearchEntireForest + public class ActionParamsClass_UpdateActiveDirectorySearchAllForestServers { - public readonly string SearchEntireForest = "SearchEntireForest"; + public readonly string SearchAllForestServers = "SearchAllForestServers"; public readonly string redirect = "redirect"; } + static readonly ActionParamsClass_SearchSubjects s_params_SearchSubjects = new ActionParamsClass_SearchSubjects(); + [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] + public ActionParamsClass_SearchSubjects SearchSubjectsParams { get { return s_params_SearchSubjects; } } + [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] + public class ActionParamsClass_SearchSubjects + { + public readonly string term = "term"; + } + static readonly ActionParamsClass_Subject s_params_Subject = new ActionParamsClass_Subject(); + [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] + public ActionParamsClass_Subject SubjectParams { get { return s_params_Subject; } } + [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] + public class ActionParamsClass_Subject + { + public readonly string Id = "Id"; + } static readonly ActionParamsClass_UpdateProxySettings s_params_UpdateProxySettings = new ActionParamsClass_UpdateProxySettings(); [GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode] public ActionParamsClass_UpdateProxySettings UpdateProxySettingsParams { get { return s_params_UpdateProxySettings; } } @@ -9555,14 +9483,14 @@ namespace Disco.Web.Areas.API.Controllers return callInfo; } - partial void UpdateActiveDirectorySearchEntireForestOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, bool SearchEntireForest, bool redirect); + partial void UpdateActiveDirectorySearchAllForestServersOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, bool SearchAllForestServers, bool redirect); - public override System.Web.Mvc.ActionResult UpdateActiveDirectorySearchEntireForest(bool SearchEntireForest, bool redirect) + public override System.Web.Mvc.ActionResult UpdateActiveDirectorySearchAllForestServers(bool SearchAllForestServers, bool redirect) { - var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.UpdateActiveDirectorySearchEntireForest); - ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "SearchEntireForest", SearchEntireForest); + var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.UpdateActiveDirectorySearchAllForestServers); + ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "SearchAllForestServers", SearchAllForestServers); ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "redirect", redirect); - UpdateActiveDirectorySearchEntireForestOverride(callInfo, SearchEntireForest, redirect); + UpdateActiveDirectorySearchAllForestServersOverride(callInfo, SearchAllForestServers, redirect); return callInfo; } @@ -9575,6 +9503,26 @@ namespace Disco.Web.Areas.API.Controllers return callInfo; } + partial void SearchSubjectsOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string term); + + public override System.Web.Mvc.ActionResult SearchSubjects(string term) + { + var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.SearchSubjects); + ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "term", term); + SearchSubjectsOverride(callInfo, term); + return callInfo; + } + + partial void SubjectOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string Id); + + public override System.Web.Mvc.ActionResult Subject(string Id) + { + var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.Subject); + ModelUnbinderHelpers.AddRouteValues(callInfo.RouteValueDictionary, "Id", Id); + SubjectOverride(callInfo, Id); + return callInfo; + } + partial void UpdateProxySettingsOverride(T4MVC_System_Web_Mvc_ActionResult callInfo, string ProxyAddress, int? ProxyPort, string ProxyUsername, string ProxyPassword, bool redirect); public override System.Web.Mvc.ActionResult UpdateProxySettings(string ProxyAddress, int? ProxyPort, string ProxyUsername, string ProxyPassword, bool redirect)