public static PluginLibraryManifestV2 UpdateManifest(DiscoDataContext Database, IScheduledTaskStatus Status) { Status.UpdateStatus(10, "Sending Request"); PluginLibraryManifestV2 result; var discoVersion = UpdateQuery.CurrentDiscoVersionFormatted(); var url = UpdateUrl(); using (var httpClient = new HttpClient()) { using (var formData = new FormUrlEncodedContent(new KeyValuePair<string, string>[] { new KeyValuePair<string, string>("DeploymentId", Database.DiscoConfiguration.DeploymentId), new KeyValuePair<string, string>("DiscoVersion", discoVersion) })) { var response = httpClient.PostAsync(url, formData).Result; response.EnsureSuccessStatusCode(); Status.UpdateStatus(50, "Waiting for Response"); var resultJson = response.Content.ReadAsStringAsync().Result; Status.UpdateStatus(90, "Processing Response"); result = JsonConvert.DeserializeObject<PluginLibraryManifestV2>(resultJson); } } var manifestJson = JsonConvert.SerializeObject(result, Formatting.Indented); var manifestFile = PluginLibrary.ManifestFilename(Database); if (!Directory.Exists(Path.GetDirectoryName(manifestFile))) Directory.CreateDirectory(Path.GetDirectoryName(manifestFile)); File.WriteAllText(manifestFile, manifestJson); return result; }
public static UpdateResponseV2 Check(DiscoDataContext Database, bool UseProxy, IScheduledTaskStatus Status) { Status.UpdateStatus(10, "Gathering statistics and building update request"); var updateRequest = BuildRequest(Database); Status.UpdateStatus(40, "Sending statistics and update request"); var discoVersion = CurrentDiscoVersionFormatted(); HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(UpdateUrl()); // Fix for Proxy Servers which don't support KeepAlive request.KeepAlive = false; if (!UseProxy) request.Proxy = new WebProxy(); request.ContentType = "application/json; charset=utf-8; encoding=gzip"; request.Method = WebRequestMethods.Http.Post; request.UserAgent = string.Format("Disco/{0} (Update)", discoVersion); using (var requestStream = request.GetRequestStream()) { using (var compressedStream = new GZipStream(requestStream, CompressionLevel.Optimal)) { using (var requestStreamWriter = new StreamWriter(compressedStream, Encoding.UTF8)) { JsonSerializer serializer = new JsonSerializer(); serializer.Serialize(requestStreamWriter, updateRequest); requestStreamWriter.Flush(); } } } Status.UpdateStatus(50, "Waiting for update response"); using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.OK) { Status.UpdateStatus(90, "Reading update response"); string updateResultJson; UpdateResponseV2 updateResult; using (var responseStream = response.GetResponseStream()) { using (var responseReader = new StreamReader(responseStream)) { updateResultJson = responseReader.ReadToEnd(); } } updateResult = JsonConvert.DeserializeObject<UpdateResponseV2>(updateResultJson); Database.DiscoConfiguration.UpdateLastCheckResponse = updateResult; Database.SaveChanges(); Status.SetFinishedMessage(string.Format("The update server reported Version {0} is the latest.", updateResult.LatestVersion)); return updateResult; } else { Status.SetTaskException(new WebException(string.Format("Server responded with: [{0}] {1}", response.StatusCode, response.StatusDescription))); return null; } } }
public static void DeleteJobQueue(DiscoDataContext Database, int JobQueueId, IScheduledTaskStatus Status) { JobQueue queue = Database.JobQueues.Find(JobQueueId); // Validate: Current Jobs? var currentJobs = Database.JobQueueJobs.Any(jqj => jqj.JobQueueId == queue.Id && !jqj.RemovedDate.HasValue); if (currentJobs) throw new InvalidOperationException("The Job Queue cannot be deleted because it contains jobs"); // Delete History Status.UpdateStatus(0, string.Format("Removing '{0}' [{1}] Job Queue", queue.Name, queue.Id), "Starting"); var jobQueueJobs = Database.JobQueueJobs.Include("Job").Where(jsj => jsj.JobQueueId == queue.Id).ToList(); if (jobQueueJobs.Count > 0) { double progressInterval = 90 / jobQueueJobs.Count; for (int jqjIndex = 0; jqjIndex < jobQueueJobs.Count; jqjIndex++) { var jqj = jobQueueJobs[jqjIndex]; Status.UpdateStatus(jqjIndex * progressInterval, string.Format("Merging history into job #{0} logs", jqj.JobId)); // Write Logs Database.JobLogs.Add(new JobLog() { JobId = jqj.JobId, TechUserId = jqj.AddedUserId, Timestamp = jqj.AddedDate, Comments = string.Format("# Added to Queue\r\n**{0}**\r\nPriority: **{1}**\r\n{2}", Environment.NewLine, queue.Name, jqj.Priority.ToString(), string.IsNullOrWhiteSpace(jqj.AddedComment) ? "<no comment>" : jqj.AddedComment) }); Database.JobLogs.Add(new JobLog() { JobId = jqj.JobId, TechUserId = jqj.RemovedUserId, Timestamp = jqj.RemovedDate.Value, Comments = string.Format("# Removed from Queue\r\n**{0}**\r\n{1}", queue.Name, string.IsNullOrWhiteSpace(jqj.RemovedComment) ? "<no comment>" : jqj.RemovedComment) }); // Delete JQJ Database.JobQueueJobs.Remove(jqj); // Save Changes Database.SaveChanges(); } } // Delete Queue Status.UpdateStatus(90, "Deleting Queue"); Database.JobQueues.Remove(queue); Database.SaveChanges(); // Remove from Cache _cache.RemoveQueue(JobQueueId); Status.Finished(string.Format("Successfully Deleted Job Queue: '{0}' [{1}]", queue.Name, queue.Id)); }
public static int ApplyRecords(this DeviceImportContext Context, DiscoDataContext Database, IScheduledTaskStatus Status) { if (Context.Records == null) throw new InvalidOperationException("Import Records have not been parsed"); if (Context.Records.Count == 0) throw new InvalidOperationException("There are no records to import"); Status.UpdateStatus(0, "Applying Import Records to Database", "Starting..."); int affectedRecords = 0; foreach (var record in Context.Records.Cast<DeviceImportRecord>().Select((r, i) => Tuple.Create(r, i))) { Status.UpdateStatus(((double)record.Item2 / Context.Records.Count) * 100, string.Format("Applying: {0}", record.Item1.DeviceSerialNumber)); if (record.Item1.Apply(Database)) affectedRecords++; } return affectedRecords; }
public static void ParseRecords(this DeviceImportContext Context, DiscoDataContext Database, IScheduledTaskStatus Status) { if (Context.Header == null) throw new InvalidOperationException("The Import Context has not been initialized"); if (Context.Header.Count == 0) throw new InvalidOperationException("No Headers were found"); if (!Context.Header.Any(h => h.Item2 == DeviceImportFieldTypes.DeviceSerialNumber)) throw new ArgumentException("At least one column must be the Device Serial Number", "Header"); if (Context.RawData == null || Context.RawData.Count == 0) throw new ArgumentException("No data was found in the import file", "RawData"); IDeviceImportCache cache; if (Context.RawData.Count > 20) cache = new DeviceImportInMemoryCache(Database); else cache = new DeviceImportDatabaseCache(Database); Context.HeaderDeviceSerialNumberIndex = Context.Header.IndexOf(Context.Header.First(h => h.Item2 == DeviceImportFieldTypes.DeviceSerialNumber)); Context.ParsedHeaders = Context.Header .Select((h, i) => Tuple.Create(h.Item1, h.Item2, i)) .Where(h => h.Item2 != DeviceImportFieldTypes.IgnoreColumn) .Select(h => new Tuple<string, DeviceImportFieldTypes, Func<string[], string>, Type>(h.Item1, h.Item2, (f) => f[h.Item3], DeviceImport.FieldHandlers.Value[h.Item2])) .ToList(); Status.UpdateStatus(0, "Parsing Import Records", "Starting..."); Context.Records = Context.RawData.Select((d, recordIndex) => { string deviceSerialNumber = Fields.DeviceSerialNumberImportField.ParseRawDeviceSerialNumber(d[Context.HeaderDeviceSerialNumberIndex]); Status.UpdateStatus(((double)recordIndex / Context.RawData.Count) * 100, string.Format("Parsing: {0}", deviceSerialNumber)); Device existingDevice = null; if (Fields.DeviceSerialNumberImportField.IsDeviceSerialNumberValid(deviceSerialNumber)) existingDevice = cache.Devices.FirstOrDefault(device => device.SerialNumber == deviceSerialNumber); var values = Context.ParsedHeaders .ToDictionary(k => k.Item2, k => k.Item3(d)); var fields = Context.ParsedHeaders.Select(h => { var f = (DeviceImportFieldBase)Activator.CreateInstance(h.Item4); f.Parse(Database, cache, Context, recordIndex, deviceSerialNumber, existingDevice, values, h.Item3(d)); return f; }).ToList(); EntityState recordAction; if (fields.Any(f => !f.FieldAction.HasValue)) recordAction = EntityState.Detached; else if (existingDevice == null) recordAction = EntityState.Added; else if (fields.Any(f => f.FieldAction == EntityState.Modified)) recordAction = EntityState.Modified; else recordAction = EntityState.Unchanged; return new DeviceImportRecord(deviceSerialNumber, fields, recordAction); }).Cast<IDeviceImportRecord>().ToList(); }
public static void UpdateLastNetworkLogonDates(DiscoDataContext Database, IScheduledTaskStatus 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<string>() { d.DistinguishedName })) .Where(d => d.Item2.Count > 0); // Determine Domain Controllers to Query IEnumerable<Tuple<ADDomain, ADDomainController, List<string>>> 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<string>("sAMAccountName"); long lastLogonValue = default(long); long lastLogonTimestampValue = default(long); lastLogonValue = result.Value<long>("lastLogon"); lastLogonTimestampValue = result.Value<long>("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; } } } }
public static void DeleteUserFlag(DiscoDataContext Database, int UserFlagId, IScheduledTaskStatus Status) { UserFlag flag = Database.UserFlags.Find(UserFlagId); // Dispose of AD Managed Groups Interop.ActiveDirectory.ActiveDirectory.Context.ManagedGroups.Remove(UserFlagUserDevicesManagedGroup.GetKey(flag)); Interop.ActiveDirectory.ActiveDirectory.Context.ManagedGroups.Remove(UserFlagUsersManagedGroup.GetKey(flag)); // Delete Assignments Status.UpdateStatus(0, string.Format("Removing '{0}' [{1}] User Flag", flag.Name, flag.Id), "Starting"); List<UserFlagAssignment> flagAssignments = Database.UserFlagAssignments.Where(fa => fa.UserFlagId == flag.Id).ToList(); if (flagAssignments.Count > 0) { Status.UpdateStatus(20, "Removing flag from users"); flagAssignments.ForEach(flagAssignment => Database.UserFlagAssignments.Remove(flagAssignment)); Database.SaveChanges(); } // Delete Flag Status.UpdateStatus(90, "Deleting User Flag"); Database.UserFlags.Remove(flag); Database.SaveChanges(); // Remove from Cache _cache.Remove(UserFlagId); Status.Finished(string.Format("Successfully Deleted User Flag: '{0}' [{1}]", flag.Name, flag.Id)); }
public static IEnumerable<UserFlagAssignment> BulkAssignOverrideUsers(DiscoDataContext Database, UserFlag UserFlag, User Technician, string Comments, List<User> Users, IScheduledTaskStatus Status) { double progressInterval; const int databaseChunkSize = 100; string comments = string.IsNullOrWhiteSpace(Comments) ? null : Comments.Trim(); Status.UpdateStatus(0, "Calculating assignment changes"); var currentAssignments = Database.UserFlagAssignments.Include("User").Where(a => a.UserFlagId == UserFlag.Id && !a.RemovedDate.HasValue).ToList(); var removeAssignments = currentAssignments.Where(ca => !Users.Any(u => u.UserId.Equals(ca.UserId, StringComparison.OrdinalIgnoreCase))).ToList(); var addUsers = Users.Where(u => !currentAssignments.Any(ca => ca.UserId.Equals(u.UserId, StringComparison.OrdinalIgnoreCase))).ToList(); if (removeAssignments.Count > 0 || addUsers.Count > 0) { progressInterval = (double)100 / (removeAssignments.Count + addUsers.Count); var removedDateTime = DateTime.Now; // Remove Assignments removeAssignments.Chunk(databaseChunkSize).SelectMany((chunk, chunkIndex) => { var chunkIndexOffset = (chunkIndex * databaseChunkSize) + removeAssignments.Count; var chunkResults = chunk.Select((flagAssignment, index) => { Status.UpdateStatus((chunkIndexOffset + index) * progressInterval, string.Format("Removing Flag: {0}", flagAssignment.User.ToString())); flagAssignment.OnRemoveUnsafe(Database, Technician); return flagAssignment; }).ToList(); // Save Chunk Items to Database Database.SaveChanges(); return chunkResults; }).ToList(); // Add Assignments var addedUserAssignments = addUsers.Chunk(databaseChunkSize).SelectMany((chunk, chunkIndex) => { var chunkIndexOffset = (chunkIndex * databaseChunkSize) + removeAssignments.Count; var chunkResults = chunk.Select((user, index) => { Status.UpdateStatus((chunkIndexOffset + index) * progressInterval, string.Format("Assigning Flag: {0}", user.ToString())); return user.OnAddUserFlag(Database, UserFlag, Technician, comments); }).ToList(); // Save Chunk Items to Database Database.SaveChanges(); return chunkResults; }).ToList(); Status.SetFinishedMessage(string.Format("{0} Users/s Added; {1} User/s Removed; {2} User/s Skipped", addUsers.Count, removeAssignments.Count, (Users.Count - addUsers.Count))); return addedUserAssignments; } else { Status.SetFinishedMessage("No changes found"); return Enumerable.Empty<UserFlagAssignment>(); } }
public static IEnumerable<UserFlagAssignment> BulkAssignAddUsers(DiscoDataContext Database, UserFlag UserFlag, User Technician, string Comments, List<User> Users, IScheduledTaskStatus Status) { if (Users.Count > 0) { double progressInterval; const int databaseChunkSize = 100; string comments = string.IsNullOrWhiteSpace(Comments) ? null : Comments.Trim(); var addUsers = Users.Where(u => !u.UserFlagAssignments.Any(a => a.UserFlagId == UserFlag.Id && !a.RemovedDate.HasValue)).ToList(); progressInterval = (double)100 / addUsers.Count; var addedUserAssignments = addUsers.Chunk(databaseChunkSize).SelectMany((chunk, chunkIndex) => { var chunkIndexOffset = databaseChunkSize * chunkIndex; var chunkResults = chunk.Select((user, index) => { Status.UpdateStatus((chunkIndexOffset + index) * progressInterval, string.Format("Assigning Flag: {0}", user.ToString())); return user.OnAddUserFlag(Database, UserFlag, Technician, comments); }).ToList(); // Save Chunk Items to Database Database.SaveChanges(); return chunkResults; }).Where(fa => fa != null).ToList(); Status.SetFinishedMessage(string.Format("{0} Users/s Added; {1} User/s Skipped", addUsers.Count, (Users.Count - addUsers.Count))); return addedUserAssignments; } else { Status.SetFinishedMessage("No changes found"); return Enumerable.Empty<UserFlagAssignment>(); } }
public static DeviceExportResult GenerateExport(DiscoDataContext Database, IQueryable<Device> Devices, DeviceExportOptions Options, IScheduledTaskStatus TaskStatus) { TaskStatus.UpdateStatus(15, "Building metadata and database query"); var metadata = Options.BuildMetadata(); if (metadata.Count == 0) throw new ArgumentException("At least one export field must be specified", "Options"); // Update Users if (Options.AssignedUserDisplayName || Options.AssignedUserSurname || Options.AssignedUserGivenName || Options.AssignedUserPhoneNumber || Options.AssignedUserEmailAddress) { TaskStatus.UpdateStatus(20, "Updating Assigned User details"); var users = Devices.Where(d => d.AssignedUserId != null).Select(d => d.AssignedUserId).Distinct().ToList(); users.Select((userId, index) => { TaskStatus.UpdateStatus(20 + (((double)20 / users.Count) * index), string.Format("Updating Assigned User details: {0}", userId)); try { return UserService.GetUser(userId, Database); } catch (Exception) { return null; } // Ignore Errors }).ToList(); } // Update Last Network Logon Date if (Options.DeviceLastNetworkLogon) { TaskStatus.UpdateStatus(40, "Updating device last network logon dates"); try { TaskStatus.IgnoreCurrentProcessChanges = true; TaskStatus.ProgressMultiplier = 20 / 100; TaskStatus.ProgressOffset = 40; Interop.ActiveDirectory.ADNetworkLogonDatesUpdateTask.UpdateLastNetworkLogonDates(Database, TaskStatus); Database.SaveChanges(); TaskStatus.IgnoreCurrentProcessChanges = false; TaskStatus.ProgressMultiplier = 1; TaskStatus.ProgressOffset = 0; } catch (Exception) { } // Ignore Errors } TaskStatus.UpdateStatus(60, "Extracting records from the database"); var records = BuildRecords(Devices).ToList(); var stream = new MemoryStream(); TaskStatus.UpdateStatus(80, string.Format("Formatting {0} records for export", records.Count)); using (StreamWriter writer = new StreamWriter(stream, Encoding.Default, 0x400, true)) { // Header writer.Write('"'); writer.Write(string.Join("\",\"", metadata.Select(m => m.Item2))); writer.Write('"'); // Records foreach (var record in records) { writer.WriteLine(); writer.Write(string.Join(",", metadata.Select(m => { var value = m.Item3(record); var isString = m.Item4; if (value == null) return null; else if (!isString) return value; else if (Options.ExcelCsvFormat) return string.Concat("=\"", value, "\""); else return string.Concat("\"", value, "\""); }))); } } stream.Position = 0; return new DeviceExportResult() { CsvResult = stream, RecordCount = records.Count }; }
public static DeviceExportResult GenerateExport(DiscoDataContext Database, DeviceExportOptions Options, IScheduledTaskStatus TaskStatus) { switch (Options.ExportType) { case DeviceExportTypes.All: return GenerateExport(Database, Database.Devices, Options, TaskStatus); case DeviceExportTypes.Batch: if (Options.ExportTypeTargetId.HasValue && Options.ExportTypeTargetId.Value > 0) return GenerateExport(Database, Database.Devices.Where(d => d.DeviceBatchId == Options.ExportTypeTargetId), Options, TaskStatus); else return GenerateExport(Database, Database.Devices.Where(d => d.DeviceBatchId == null), Options, TaskStatus); case DeviceExportTypes.Model: return GenerateExport(Database, Database.Devices.Where(d => d.DeviceModelId == Options.ExportTypeTargetId), Options, TaskStatus); case DeviceExportTypes.Profile: return GenerateExport(Database, Database.Devices.Where(d => d.DeviceProfileId == Options.ExportTypeTargetId), Options, TaskStatus); default: throw new ArgumentException(string.Format("Unknown Device Export Type", Options.ExportType.ToString()), "Options"); } }