Certificate/wireless plugins; major refactoring
Migrate much of BI to Services. Added Wireless Profile Provider plugin feature. Added Certificate Authority Provider plugin feature. Modified Certificate Provider plugin feature. Database migration v17, for Device Profiles. Enrolment Client Updated to support CA Certificates, Wireless Profiles and Hardware Info. New Client Enrolment Protocol to support new features. Plugin Manifest Generator added to main solution. Improved AD search performance.
This commit is contained in:
@@ -1,18 +1,95 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Documents.ManagedGroups;
|
||||
using Disco.Services.Users;
|
||||
using Exceptionless;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class AttachmentActionExtensions
|
||||
{
|
||||
#region Delete
|
||||
public static bool CanDelete(this DeviceAttachment da)
|
||||
{
|
||||
if (UserService.CurrentAuthorization.Has(Claims.Device.Actions.RemoveAnyAttachments))
|
||||
return true;
|
||||
|
||||
if (UserService.CurrentAuthorization.Has(Claims.Device.Actions.RemoveOwnAttachments)
|
||||
&& da.TechUserId.Equals(UserService.CurrentUserId, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
public static void OnDelete(this DeviceAttachment da, DiscoDataContext Database)
|
||||
{
|
||||
if (!da.CanDelete())
|
||||
throw new InvalidOperationException("Deletion of Attachment is Denied");
|
||||
|
||||
var attachmentId = da.Id;
|
||||
var documentTemplateId = da.DocumentTemplateId;
|
||||
var deviceSerialNumber = da.DeviceSerialNumber;
|
||||
|
||||
da.RepositoryDelete(Database);
|
||||
Database.DeviceAttachments.Remove(da);
|
||||
|
||||
DocumentTemplateManagedGroups.TriggerDeviceAttachmentDeleted(Database, attachmentId, documentTemplateId, deviceSerialNumber);
|
||||
}
|
||||
public static bool CanDelete(this JobAttachment ja)
|
||||
{
|
||||
if (UserService.CurrentAuthorization.Has(Claims.Job.Actions.RemoveAnyAttachments))
|
||||
return true;
|
||||
|
||||
if (UserService.CurrentAuthorization.Has(Claims.Job.Actions.RemoveOwnAttachments)
|
||||
&& ja.TechUserId.Equals(UserService.CurrentUserId, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
public static void OnDelete(this JobAttachment ja, DiscoDataContext Database)
|
||||
{
|
||||
if (!ja.CanDelete())
|
||||
throw new InvalidOperationException("Deletion of Attachment is Denied");
|
||||
|
||||
var attachmentId = ja.Id;
|
||||
var documentTemplateId = ja.DocumentTemplateId;
|
||||
var jobId = ja.JobId;
|
||||
|
||||
ja.RepositoryDelete(Database);
|
||||
Database.JobAttachments.Remove(ja);
|
||||
|
||||
DocumentTemplateManagedGroups.TriggerJobAttachmentDeleted(Database, attachmentId, documentTemplateId, jobId);
|
||||
}
|
||||
public static bool CanDelete(this UserAttachment ua)
|
||||
{
|
||||
if (UserService.CurrentAuthorization.Has(Claims.User.Actions.RemoveAnyAttachments))
|
||||
return true;
|
||||
|
||||
if (UserService.CurrentAuthorization.Has(Claims.User.Actions.RemoveOwnAttachments)
|
||||
&& ua.TechUserId.Equals(UserService.CurrentUserId, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
public static void OnDelete(this UserAttachment ua, DiscoDataContext Database)
|
||||
{
|
||||
if (!ua.CanDelete())
|
||||
throw new InvalidOperationException("Deletion of Attachment is Denied");
|
||||
|
||||
var attachmentId = ua.Id;
|
||||
var documentTemplateId = ua.DocumentTemplateId;
|
||||
var userId = ua.UserId;
|
||||
|
||||
ua.RepositoryDelete(Database);
|
||||
Database.UserAttachments.Remove(ua);
|
||||
|
||||
DocumentTemplateManagedGroups.TriggerUserAttachmentDeleted(Database, attachmentId, documentTemplateId, userId);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static DeviceAttachment CreateAttachment(this Device Device, DiscoDataContext Database, User CreatorUser, string Filename, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, Image PdfThumbnail = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.OrdinalIgnoreCase))
|
||||
@@ -149,8 +226,8 @@ namespace Disco.Services
|
||||
{
|
||||
using (Image sourceImage = Image.FromStream(Source))
|
||||
{
|
||||
Thumbnail = sourceImage.ResizeImage(48, 48);
|
||||
using (Image mimeTypeIcon = Disco.Services.Properties.Resources.MimeType_img16)
|
||||
Thumbnail = sourceImage.ResizeImage(48, 48, Brushes.Black);
|
||||
using (Image mimeTypeIcon = Properties.Resources.MimeType_img16)
|
||||
{
|
||||
Thumbnail.EmbedIconOverlay(mimeTypeIcon);
|
||||
}
|
||||
@@ -177,8 +254,15 @@ namespace Disco.Services
|
||||
var pageSize = pdfiumDocument.PageSizes[0];
|
||||
var size = ImagingExtensions.CalculateResize((int)pageSize.Width, (int)pageSize.Height, 48, 48);
|
||||
|
||||
Thumbnail = pdfiumDocument.Render(0, (int)size.Width, (int)size.Height, 72, 72, true);
|
||||
return true;
|
||||
using (var sourceImage = pdfiumDocument.Render(0, (int)size.Width, (int)size.Height, 72, 72, true))
|
||||
{
|
||||
Thumbnail = sourceImage.ResizeImage(48, 48, Brushes.White);
|
||||
using (Image mimeTypeIcon = Properties.Resources.MimeType_pdf16)
|
||||
{
|
||||
Thumbnail.EmbedIconOverlay(mimeTypeIcon);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Authorization;
|
||||
using Disco.Services.Users;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class AuthorizationRoleExtensions
|
||||
{
|
||||
|
||||
public static void Delete(this IRoleToken roleToken, DiscoDataContext Database)
|
||||
{
|
||||
var role = Database.AuthorizationRoles.Find(roleToken.Role.Id);
|
||||
UserService.DeleteAuthorizationRole(Database, roleToken.Role);
|
||||
}
|
||||
|
||||
public static void Delete(this AuthorizationRole role, DiscoDataContext Database)
|
||||
{
|
||||
UserService.DeleteAuthorizationRole(Database, role);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.ClientServices;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Devices.Enrolment;
|
||||
using Disco.Services.Users;
|
||||
using System;
|
||||
using System.Web;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class ClientServicesExtensions
|
||||
{
|
||||
|
||||
public static EnrolResponse BuildResponse(this Enrol request)
|
||||
{
|
||||
if (HttpContext.Current == null)
|
||||
throw new PlatformNotSupportedException("This function can only be accessed from within ASP.NET");
|
||||
|
||||
string username = null;
|
||||
if (HttpContext.Current.Request.IsAuthenticated)
|
||||
username = HttpContext.Current.User.Identity.Name;
|
||||
|
||||
using (DiscoDataContext database = new DiscoDataContext())
|
||||
{
|
||||
EnrolResponse response = DeviceEnrolment.Enrol(database, username, request);
|
||||
database.SaveChanges();
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
public static WhoAmIResponse BuildResponse(this WhoAmI request)
|
||||
{
|
||||
if (HttpContext.Current == null)
|
||||
throw new PlatformNotSupportedException("This function can only be accessed from within ASP.NET");
|
||||
|
||||
string username = null;
|
||||
if (HttpContext.Current.Request.IsAuthenticated)
|
||||
username = HttpContext.Current.User.Identity.Name;
|
||||
|
||||
if (username == null)
|
||||
throw new InvalidOperationException("Unauthenticated Http Context");
|
||||
|
||||
using (DiscoDataContext database = new DiscoDataContext())
|
||||
{
|
||||
AuthorizationToken token = UserService.GetAuthorization(username, database, true);
|
||||
|
||||
WhoAmIResponse response = new WhoAmIResponse()
|
||||
{
|
||||
Username = token.User.UserId,
|
||||
DisplayName = token.User.DisplayName,
|
||||
Type = token.Has(Claims.ComputerAccount) ? "Computer Account" : "User Account"
|
||||
};
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
public static MacEnrolResponse BuildResponse(this MacEnrol request)
|
||||
{
|
||||
if (HttpContext.Current == null)
|
||||
throw new PlatformNotSupportedException("This function can only be accessed from within ASP.NET");
|
||||
|
||||
using (DiscoDataContext database = new DiscoDataContext())
|
||||
{
|
||||
MacEnrolResponse response = DeviceEnrolment.MacEnrol(database, request, false);
|
||||
database.SaveChanges();
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using Disco.Services.Users;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class DeviceActionExtensions
|
||||
{
|
||||
|
||||
public static bool IsDecommissioned(this Device d)
|
||||
{
|
||||
return d.DecommissionedDate.HasValue;
|
||||
}
|
||||
|
||||
public static bool CanCreateJob(this Device d)
|
||||
{
|
||||
if (!JobActionExtensions.CanCreate())
|
||||
return false;
|
||||
|
||||
return !d.IsDecommissioned();
|
||||
}
|
||||
|
||||
public static bool CanUpdateAssignment(this Device d)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Device.Actions.AssignUser))
|
||||
return false;
|
||||
|
||||
return !d.IsDecommissioned();
|
||||
}
|
||||
|
||||
public static bool CanUpdateDeviceProfile(this Device d)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Device.Properties.DeviceProfile))
|
||||
return false;
|
||||
|
||||
return !d.IsDecommissioned();
|
||||
}
|
||||
|
||||
public static bool CanUpdateDeviceBatch(this Device d)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Device.Properties.DeviceBatch))
|
||||
return false;
|
||||
|
||||
return !d.IsDecommissioned();
|
||||
}
|
||||
|
||||
public static bool CanUpdateTrustEnrol(this Device d)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Device.Actions.AllowUnauthenticatedEnrol))
|
||||
return false;
|
||||
|
||||
return !d.IsDecommissioned() && !d.AllowUnauthenticatedEnrol;
|
||||
}
|
||||
public static bool CanUpdateUntrustEnrol(this Device d)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Device.Actions.AllowUnauthenticatedEnrol))
|
||||
return false;
|
||||
|
||||
return !d.IsDecommissioned() && d.AllowUnauthenticatedEnrol;
|
||||
}
|
||||
|
||||
#region Decommission
|
||||
public static bool CanDecommission(this Device d)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Device.Actions.Decommission))
|
||||
return false;
|
||||
|
||||
if (d.DecommissionedDate.HasValue)
|
||||
return false; // Already Decommissioned
|
||||
|
||||
if (d.AssignedUserId != null)
|
||||
return false; // User Assigned to Device
|
||||
|
||||
if (d.Jobs.Count(j => !j.ClosedDate.HasValue) > 0)
|
||||
return false; // Device linked to > 0 Open Jobs
|
||||
|
||||
return true;
|
||||
}
|
||||
public static void OnDecommission(this Device d, Disco.Models.Repository.DecommissionReasons Reason)
|
||||
{
|
||||
if (!d.CanDecommission())
|
||||
throw new InvalidOperationException("Decommission of Device is Denied");
|
||||
|
||||
d.DecommissionedDate = DateTime.Now;
|
||||
d.DecommissionReason = Reason;
|
||||
|
||||
// Disable AD Account
|
||||
if (ActiveDirectory.IsValidDomainAccountId(d.DeviceDomainId))
|
||||
{
|
||||
var adAccount = d.ActiveDirectoryAccount();
|
||||
if (adAccount != null && !adAccount.IsCriticalSystemObject)
|
||||
{
|
||||
adAccount.DisableAccount();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Recommission
|
||||
public static bool CanRecommission(this Device d)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Device.Actions.Recommission))
|
||||
return false;
|
||||
|
||||
return d.DecommissionedDate.HasValue;
|
||||
}
|
||||
public static void OnRecommission(this Device d)
|
||||
{
|
||||
if (!d.CanRecommission())
|
||||
throw new InvalidOperationException("Recommission of Device is Denied");
|
||||
|
||||
d.DecommissionedDate = null;
|
||||
d.DecommissionReason = null;
|
||||
|
||||
// Enable AD Account
|
||||
if (ActiveDirectory.IsValidDomainAccountId(d.DeviceDomainId))
|
||||
{
|
||||
var adAccount = d.ActiveDirectoryAccount();
|
||||
if (adAccount != null && !adAccount.IsCriticalSystemObject)
|
||||
{
|
||||
adAccount.EnableAccount();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Delete
|
||||
public static bool CanDelete(this Device d)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Device.Actions.Delete))
|
||||
return false;
|
||||
|
||||
return d.DecommissionedDate.HasValue;
|
||||
}
|
||||
public static void OnDelete(this Device d, DiscoDataContext Database)
|
||||
{
|
||||
// Delete Jobs
|
||||
foreach (Job j in Database.Jobs.Where(i => i.DeviceSerialNumber == d.SerialNumber))
|
||||
{
|
||||
if (j.UserId == null)
|
||||
{ // No User associated, thus must Delete whole Job
|
||||
if (j.CanDelete())
|
||||
j.OnDelete(Database);
|
||||
else
|
||||
throw new InvalidOperationException(string.Format("Deletion of Device is Denied (See Job# {0})", j.Id));
|
||||
}
|
||||
else
|
||||
{
|
||||
// User associated to Job, thus just remove Devices' association
|
||||
j.DeviceSerialNumber = null;
|
||||
|
||||
// Write Job Log
|
||||
JobLog jobLog = new JobLog()
|
||||
{
|
||||
JobId = j.Id,
|
||||
TechUserId = UserService.CurrentUser.UserId,
|
||||
Timestamp = DateTime.Now,
|
||||
Comments = string.Format("# Device Deleted\r\n\r\nSerial Number: **{0}**\r\nComputer Name: **{1}**\r\nModel: **{2}**\r\nProfile: **{3}**",
|
||||
d.SerialNumber, d.DeviceDomainId, d.DeviceModel, d.DeviceProfile)
|
||||
};
|
||||
Database.JobLogs.Add(jobLog);
|
||||
}
|
||||
}
|
||||
|
||||
// Disable Wireless Certificates
|
||||
foreach (var wc in Database.DeviceCertificates.Where(i => i.DeviceSerialNumber == d.SerialNumber))
|
||||
{
|
||||
wc.DeviceSerialNumber = null;
|
||||
wc.Enabled = false;
|
||||
}
|
||||
// Delete Device Details
|
||||
foreach (var dd in Database.DeviceDetails.Where(i => i.DeviceSerialNumber == d.SerialNumber))
|
||||
Database.DeviceDetails.Remove(dd);
|
||||
// Delete Device Attachments
|
||||
foreach (var da in Database.DeviceAttachments.Where(i => i.DeviceSerialNumber == d.SerialNumber))
|
||||
{
|
||||
da.RepositoryDelete(Database);
|
||||
Database.DeviceAttachments.Remove(da);
|
||||
}
|
||||
// Delete Device User Assignments
|
||||
foreach (var dua in Database.DeviceUserAssignments.Where(i => i.DeviceSerialNumber == d.SerialNumber))
|
||||
Database.DeviceUserAssignments.Remove(dua);
|
||||
|
||||
Database.Devices.Remove(d);
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Devices.ManagedGroups;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using Disco.Services.Users;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class DeviceBatchExtensions
|
||||
{
|
||||
public static bool CanDelete(this DeviceBatch db, DiscoDataContext Database)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Config.DeviceBatch.Delete))
|
||||
return false;
|
||||
|
||||
// Can't Delete if Contains Devices
|
||||
var deviceCount = Database.Devices.Count(d => d.DeviceBatchId == db.Id);
|
||||
if (deviceCount > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void Delete(this DeviceBatch db, DiscoDataContext Database)
|
||||
{
|
||||
if (!db.CanDelete(Database))
|
||||
throw new InvalidOperationException("The state of this Device Batch doesn't allow it to be deleted");
|
||||
|
||||
// Remove Linked Group
|
||||
ActiveDirectory.Context.ManagedGroups.Remove(DeviceBatchDevicesManagedGroup.GetKey(db));
|
||||
ActiveDirectory.Context.ManagedGroups.Remove(DeviceBatchAssignedUsersManagedGroup.GetKey(db));
|
||||
|
||||
// Delete Batch
|
||||
Database.DeviceBatches.Remove(db);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using System;
|
||||
|
||||
namespace Disco.Services.Devices
|
||||
{
|
||||
public static class DeviceBatches
|
||||
{
|
||||
|
||||
public static DeviceBatch DefaultNewDeviceBatch(DiscoDataContext Database)
|
||||
{
|
||||
return new DeviceBatch()
|
||||
{
|
||||
PurchaseDate = DateTime.Today
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.ClientServices;
|
||||
using Disco.Models.ClientServices.EnrolmentInformation;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Plugins.Features.CertificateAuthorityProvider;
|
||||
using Disco.Services.Plugins.Features.CertificateProvider;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class DeviceCertificateExtensions
|
||||
{
|
||||
public static CertificateStore ProvisionCertificates(this Device device, DiscoDataContext Database, Enrol Enrolment, out List<DeviceCertificate> ProvisionedCertificates)
|
||||
{
|
||||
var personalCertificates = new List<byte[]>();
|
||||
var personalCertificatesRemove = new List<string>();
|
||||
|
||||
var intermediateCertificates = new List<byte[]>();
|
||||
var intermediateCertificatesRemove = new List<string>();
|
||||
|
||||
var trustedRootCertificates = new List<byte[]>();
|
||||
var trustedRootCertificatesRemove = new List<string>();
|
||||
|
||||
ProvisionedCertificates = new List<DeviceCertificate>();
|
||||
|
||||
foreach (var pluginFeature in device.DeviceProfile.GetCertificateProviders())
|
||||
{
|
||||
using (var providerFeature = pluginFeature.CreateInstance<CertificateProviderFeature>())
|
||||
{
|
||||
var personalResult = providerFeature.ProvisionPersonalCertificate(Database, device, Enrolment);
|
||||
|
||||
if (personalResult != null)
|
||||
{
|
||||
if (personalResult.AllocatedCertificates != null && personalResult.AllocatedCertificates.Count > 0)
|
||||
{
|
||||
// Avoid transporting certificates if they are already installed
|
||||
foreach (var certificate in personalResult.AllocatedCertificates)
|
||||
{
|
||||
var x509Certificate = new X509Certificate2(certificate.Content, "password");
|
||||
if (!Enrolment.Certificates.Any(c => c.Thumbprint.Equals(x509Certificate.Thumbprint, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
personalCertificates.Add(certificate.Content);
|
||||
ProvisionedCertificates.Add(certificate);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (personalResult.RemoveCertificateThumbprints != null && personalResult.RemoveCertificateThumbprints.Count > 0)
|
||||
{
|
||||
personalCertificatesRemove.AddRange(personalResult.RemoveCertificateThumbprints);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var pluginFeature in device.DeviceProfile.GetCertificateAuthorityProviders())
|
||||
{
|
||||
using (var providerFeature = pluginFeature.CreateInstance<CertificateAuthorityProviderFeature>())
|
||||
{
|
||||
var caResult = providerFeature.ProvisionAuthorityCertificates(Database, device, Enrolment);
|
||||
|
||||
if (caResult.TrustedRootCertificates != null && caResult.TrustedRootCertificates.Count > 0)
|
||||
{
|
||||
trustedRootCertificates.AddRange(caResult.TrustedRootCertificates);
|
||||
}
|
||||
if (caResult.TrustedRootCertificateRemoveThumbprints != null && caResult.TrustedRootCertificateRemoveThumbprints.Count > 0)
|
||||
{
|
||||
trustedRootCertificatesRemove.AddRange(caResult.TrustedRootCertificateRemoveThumbprints);
|
||||
}
|
||||
if (caResult.IntermediateCertificates != null && caResult.IntermediateCertificates.Count > 0)
|
||||
{
|
||||
intermediateCertificates.AddRange(caResult.IntermediateCertificates);
|
||||
}
|
||||
if (caResult.IntermediateCertificateRemoveThumbprints != null && caResult.IntermediateCertificateRemoveThumbprints.Count > 0)
|
||||
{
|
||||
intermediateCertificatesRemove.AddRange(caResult.IntermediateCertificateRemoveThumbprints);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (personalCertificates.Count == 0 && personalCertificatesRemove.Count == 0 &&
|
||||
intermediateCertificates.Count == 0 && intermediateCertificatesRemove.Count == 0 &&
|
||||
trustedRootCertificates.Count == 0 && trustedRootCertificatesRemove.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new CertificateStore()
|
||||
{
|
||||
TrustedRootCertificates = trustedRootCertificates.Count > 0 ? trustedRootCertificates : null,
|
||||
TrustedRootRemoveThumbprints = trustedRootCertificatesRemove.Count > 0 ? trustedRootCertificatesRemove : null,
|
||||
|
||||
IntermediateCertificates = intermediateCertificates.Count > 0 ? intermediateCertificates : null,
|
||||
IntermediateRemoveThumbprints = intermediateCertificatesRemove.Count > 0 ? intermediateCertificatesRemove : null,
|
||||
|
||||
PersonalCertificates = personalCertificates.Count > 0 ? personalCertificates : null,
|
||||
PersonalRemoveThumbprints = personalCertificatesRemove.Count > 0 ? personalCertificatesRemove : null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static DateTime? CertificateExpirationDate(this DeviceCertificate wc)
|
||||
{
|
||||
if (wc.Content == null || wc.Content.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var c = new X509Certificate2(wc.Content, "password");
|
||||
return c.NotAfter;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
using Disco.Models.Repository;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class DeviceDetailExtensions
|
||||
{
|
||||
#region Helpers
|
||||
private static string GetDetail(this IEnumerable<DeviceDetail> details, string Scope, string Key)
|
||||
{
|
||||
if (details == null)
|
||||
throw new ArgumentNullException("details");
|
||||
if (string.IsNullOrEmpty(Scope))
|
||||
throw new ArgumentNullException("Scope");
|
||||
if (string.IsNullOrEmpty(Key))
|
||||
throw new ArgumentNullException("Key");
|
||||
|
||||
var detail = details.Where(d => d.Key == Key).FirstOrDefault();
|
||||
|
||||
if (detail == null)
|
||||
return null;
|
||||
else
|
||||
return detail.Value;
|
||||
}
|
||||
|
||||
private static void SetDetail(this Device device, string Scope, string Key, string Value)
|
||||
{
|
||||
if (device == null)
|
||||
throw new ArgumentNullException("device");
|
||||
if (string.IsNullOrEmpty(Scope))
|
||||
throw new ArgumentNullException("Scope");
|
||||
if (string.IsNullOrEmpty(Key))
|
||||
throw new ArgumentNullException("Key");
|
||||
|
||||
var detail = device.DeviceDetails.Where(d => d.Scope == Scope && d.Key == Key).FirstOrDefault();
|
||||
|
||||
// No Detail Stored & Set to Null
|
||||
if (detail == null && Value == null)
|
||||
return;
|
||||
|
||||
if (detail == null)
|
||||
{
|
||||
detail = new DeviceDetail()
|
||||
{
|
||||
DeviceSerialNumber = device.SerialNumber,
|
||||
Scope = Scope,
|
||||
Key = Key,
|
||||
Value = Value
|
||||
};
|
||||
device.DeviceDetails.Add(detail);
|
||||
}
|
||||
|
||||
if (detail.Value != Value)
|
||||
{
|
||||
if (Value == null)
|
||||
{
|
||||
device.DeviceDetails.Remove(detail);
|
||||
}
|
||||
else
|
||||
{
|
||||
detail.Value = Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region LanMacAddress
|
||||
/// <summary>
|
||||
/// Gets the LanMacAddress Device Detail Value
|
||||
/// </summary>
|
||||
/// <returns>The LanMacAddress or null</returns>
|
||||
public static string LanMacAddress(this IEnumerable<DeviceDetail> details)
|
||||
{
|
||||
return details.GetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyLanMacAddress);
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the LanMacAddress Device Detail Value
|
||||
/// </summary>
|
||||
public static void LanMacAddress(this IEnumerable<DeviceDetail> details, Device device, string LanMacAddress)
|
||||
{
|
||||
device.SetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyLanMacAddress, LanMacAddress);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region WLanMacAddress
|
||||
/// <summary>
|
||||
/// Gets the WLanMacAddress Device Detail Value
|
||||
/// </summary>
|
||||
/// <returns>The WLanMacAddress or null</returns>
|
||||
public static string WLanMacAddress(this IEnumerable<DeviceDetail> details)
|
||||
{
|
||||
return details.GetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyWLanMacAddress);
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the WLanMacAddress Device Detail Value
|
||||
/// </summary>
|
||||
public static void WLanMacAddress(this IEnumerable<DeviceDetail> details, Device device, string WLanMacAddress)
|
||||
{
|
||||
device.SetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyWLanMacAddress, WLanMacAddress);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ACAdapter
|
||||
/// <summary>
|
||||
/// Gets the ACAdapter Device Detail Value
|
||||
/// </summary>
|
||||
/// <returns>The ACAdapter or null</returns>
|
||||
public static string ACAdapter(this IEnumerable<DeviceDetail> details)
|
||||
{
|
||||
return details.GetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyACAdapter);
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the ACAdapter Device Detail Value
|
||||
/// </summary>
|
||||
public static void ACAdapter(this IEnumerable<DeviceDetail> details, Device device, string ACAdapter)
|
||||
{
|
||||
device.SetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyACAdapter, ACAdapter);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Battery
|
||||
/// <summary>
|
||||
/// Gets the Battery Device Detail Value
|
||||
/// </summary>
|
||||
/// <returns>The Battery or null</returns>
|
||||
public static string Battery(this IEnumerable<DeviceDetail> details)
|
||||
{
|
||||
return details.GetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyBattery);
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the Battery Device Detail Value
|
||||
/// </summary>
|
||||
public static void Battery(this IEnumerable<DeviceDetail> details, Device device, string Battery)
|
||||
{
|
||||
device.SetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyBattery, Battery);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Keyboard
|
||||
/// <summary>
|
||||
/// Gets the Keyboard Device Detail Value
|
||||
/// </summary>
|
||||
/// <returns>The Keyboard or null</returns>
|
||||
public static string Keyboard(this IEnumerable<DeviceDetail> details)
|
||||
{
|
||||
return details.GetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyKeyboard);
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the Keyboard Device Detail Value
|
||||
/// </summary>
|
||||
public static void Keyboard(this IEnumerable<DeviceDetail> details, Device device, string Keyboard)
|
||||
{
|
||||
device.SetDetail(DeviceDetail.ScopeHardware, DeviceDetail.HardwareKeyKeyboard, Keyboard);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Documents;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Expressions;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using Disco.Services.Users;
|
||||
using Exceptionless;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class DeviceExtensions
|
||||
{
|
||||
public static string ComputerNameRender(this Device device, DiscoDataContext Database, ADDomain Domain)
|
||||
{
|
||||
if (Domain == null)
|
||||
throw new ArgumentNullException("Domain");
|
||||
|
||||
var deviceProfile = device.DeviceProfile;
|
||||
Expression computerNameTemplateExpression = null;
|
||||
computerNameTemplateExpression = ExpressionCache.GetValue(DeviceProfileExtensions.ComputerNameExpressionCacheModule, deviceProfile.Id.ToString(), () =>
|
||||
{
|
||||
// Removed 2012-06-14 G# - Properties moved to DeviceProfile model & DB Migrated in DBv3.
|
||||
//return Expressions.Expression.TokenizeSingleDynamic(null, deviceProfile.Configuration(context).ComputerNameTemplate, 0);
|
||||
return Expression.TokenizeSingleDynamic(null, deviceProfile.ComputerNameTemplate, 0);
|
||||
});
|
||||
var evaluatorVariables = Expression.StandardVariables(null, Database, UserService.CurrentUser, DateTime.Now, null);
|
||||
string rendered;
|
||||
try
|
||||
{
|
||||
rendered = computerNameTemplateExpression.EvaluateFirst<string>(device, evaluatorVariables);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ex.ToExceptionless().AddObject(deviceProfile.ComputerNameTemplate, "ComputerNameTemplate").Submit();
|
||||
throw new InvalidOperationException(string.Format("An error occurred rendering the computer name: [{0}] {1}", ex.GetType().Name, ex.Message), ex.InnerException);
|
||||
}
|
||||
if (rendered == null || rendered.Length > 24)
|
||||
{
|
||||
throw new InvalidOperationException("The rendered computer name would be invalid or longer than 24 characters");
|
||||
}
|
||||
|
||||
return string.Format(@"{0}\{1}", Domain.NetBiosName, rendered);
|
||||
}
|
||||
public static List<DocumentTemplate> AvailableDocumentTemplates(this Device d, DiscoDataContext Database, User User, DateTime TimeStamp)
|
||||
{
|
||||
List<DocumentTemplate> ats = Database.DocumentTemplates
|
||||
.Where(at => at.Scope == DocumentTemplate.DocumentTemplateScopes.Device).ToList();
|
||||
|
||||
return ats.Where(at => at.FilterExpressionMatches(d, Database, User, TimeStamp, DocumentState.DefaultState())).ToList();
|
||||
}
|
||||
|
||||
public static bool UpdateLastNetworkLogonDate(this Device Device)
|
||||
{
|
||||
return ADNetworkLogonDatesUpdateTask.UpdateLastNetworkLogonDate(Device);
|
||||
}
|
||||
|
||||
public static Device AddOffline(this Device d, DiscoDataContext Database)
|
||||
{
|
||||
// Just Include:
|
||||
// - Serial Number
|
||||
// - Asset Number
|
||||
// - Profile Id
|
||||
// - Assigned User Id
|
||||
// - Batch
|
||||
|
||||
// Enforce Authorization
|
||||
var auth = UserService.CurrentAuthorization;
|
||||
if (!auth.Has(Claims.Device.Properties.AssetNumber))
|
||||
d.AssetNumber = null;
|
||||
if (!auth.Has(Claims.Device.Properties.Location))
|
||||
d.Location = null;
|
||||
if (!auth.Has(Claims.Device.Properties.DeviceBatch))
|
||||
d.DeviceBatchId = null;
|
||||
if (!auth.Has(Claims.Device.Properties.DeviceProfile))
|
||||
d.DeviceProfileId = Database.DiscoConfiguration.DeviceProfiles.DefaultAddDeviceOfflineDeviceProfileId;
|
||||
if (!auth.Has(Claims.Device.Actions.AssignUser))
|
||||
d.AssignedUserId = null;
|
||||
|
||||
|
||||
// Batch
|
||||
DeviceBatch db = default(DeviceBatch);
|
||||
if (d.DeviceBatchId.HasValue)
|
||||
db = Database.DeviceBatches.Find(d.DeviceBatchId.Value);
|
||||
|
||||
// Default Device Model
|
||||
DeviceModel dm = default(DeviceModel);
|
||||
if (db != null && db.DefaultDeviceModelId.HasValue)
|
||||
dm = Database.DeviceModels.Find(db.DefaultDeviceModelId); // From Batch
|
||||
else
|
||||
dm = Database.DeviceModels.Find(1); // Default
|
||||
|
||||
Device d2 = new Device()
|
||||
{
|
||||
SerialNumber = d.SerialNumber.ToUpper(),
|
||||
AssetNumber = d.AssetNumber,
|
||||
Location = d.Location,
|
||||
CreatedDate = DateTime.Now,
|
||||
DeviceProfileId = d.DeviceProfileId,
|
||||
DeviceProfile = Database.DeviceProfiles.Find(d.DeviceProfileId),
|
||||
AllowUnauthenticatedEnrol = true,
|
||||
DeviceModelId = dm.Id,
|
||||
DeviceModel = dm,
|
||||
DeviceBatchId = d.DeviceBatchId,
|
||||
DeviceBatch = db
|
||||
};
|
||||
|
||||
Database.Devices.Add(d2);
|
||||
if (!string.IsNullOrEmpty(d.AssignedUserId))
|
||||
{
|
||||
User u = UserService.GetUser(ActiveDirectory.ParseDomainAccountId(d.AssignedUserId), Database, true);
|
||||
d2.AssignDevice(Database, u);
|
||||
}
|
||||
|
||||
return d2;
|
||||
}
|
||||
|
||||
public static DeviceUserAssignment AssignDevice(this Device d, DiscoDataContext Database, User u)
|
||||
{
|
||||
DeviceUserAssignment newDua = default(DeviceUserAssignment);
|
||||
|
||||
// Mark existing assignments as Unassigned
|
||||
foreach (var dua in Database.DeviceUserAssignments.Where(m => m.DeviceSerialNumber == d.SerialNumber && !m.UnassignedDate.HasValue))
|
||||
dua.UnassignedDate = DateTime.Now;
|
||||
|
||||
if (u != null)
|
||||
{
|
||||
// Add new Assignment
|
||||
newDua = new DeviceUserAssignment()
|
||||
{
|
||||
DeviceSerialNumber = d.SerialNumber,
|
||||
AssignedUserId = u.UserId,
|
||||
AssignedDate = DateTime.Now
|
||||
};
|
||||
Database.DeviceUserAssignments.Add(newDua);
|
||||
|
||||
d.AssignedUserId = u.UserId;
|
||||
d.AssignedUser = u;
|
||||
}
|
||||
else
|
||||
{
|
||||
d.AssignedUserId = null;
|
||||
}
|
||||
|
||||
// Update AD Account
|
||||
if (ActiveDirectory.IsValidDomainAccountId(d.DeviceDomainId))
|
||||
{
|
||||
var adMachineAccount = ActiveDirectory.RetrieveADMachineAccount(d.DeviceDomainId);
|
||||
if (adMachineAccount != null)
|
||||
adMachineAccount.SetDescription(d);
|
||||
}
|
||||
|
||||
return newDua;
|
||||
}
|
||||
|
||||
public static string ReasonMessage(this DecommissionReasons r)
|
||||
{
|
||||
switch (r)
|
||||
{
|
||||
case DecommissionReasons.EndOfLife:
|
||||
return "End of Life";
|
||||
case DecommissionReasons.Sold:
|
||||
return "Sold";
|
||||
case DecommissionReasons.Stolen:
|
||||
return "Stolen";
|
||||
case DecommissionReasons.Lost:
|
||||
return "Lost";
|
||||
case DecommissionReasons.Damaged:
|
||||
return "Damaged";
|
||||
case DecommissionReasons.Donated:
|
||||
return "Donated";
|
||||
case DecommissionReasons.Returned:
|
||||
return "Returned";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReasonMessage(this DecommissionReasons? r)
|
||||
{
|
||||
if (!r.HasValue)
|
||||
return "Not Decommissioned";
|
||||
|
||||
return r.Value.ReasonMessage();
|
||||
}
|
||||
|
||||
public static string StatusCode(this Device Device)
|
||||
{
|
||||
if (Device.DecommissionedDate.HasValue)
|
||||
return "Decommissioned";
|
||||
|
||||
if (!Device.EnrolledDate.HasValue)
|
||||
return "NotEnrolled";
|
||||
|
||||
return "Active";
|
||||
}
|
||||
|
||||
public static string Status(this Device Device)
|
||||
{
|
||||
if (Device.DecommissionedDate.HasValue)
|
||||
return string.Format("Decommissioned ({0})", Device.DecommissionReason.ReasonMessage());
|
||||
|
||||
if (!Device.EnrolledDate.HasValue)
|
||||
return "Not Enrolled";
|
||||
|
||||
return "Active";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Users;
|
||||
using System;
|
||||
using System.Data.Entity;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class DeviceModelExtensions
|
||||
{
|
||||
private static object _CreateDeviceModelLock = new object();
|
||||
|
||||
public static bool CanDelete(this DeviceModel dm, DiscoDataContext Database)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Config.DeviceModel.Delete))
|
||||
return false;
|
||||
|
||||
// Can't Delete Default Model (Id: 1)
|
||||
if (dm.Id == 1)
|
||||
return false;
|
||||
|
||||
// Can't Delete if Contains Devices
|
||||
if (Database.Devices.Count(d => d.DeviceModelId == dm.Id) > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
public static void Delete(this DeviceModel dm, DiscoDataContext Database)
|
||||
{
|
||||
if (!dm.CanDelete(Database))
|
||||
throw new InvalidOperationException("The state of this Device Model doesn't allow it to be deleted");
|
||||
|
||||
// Delete Image
|
||||
var deviceModelImagePath = dm.ImageFilePath();
|
||||
if (File.Exists(deviceModelImagePath))
|
||||
File.Delete(deviceModelImagePath);
|
||||
|
||||
// Delete any Device Model Components
|
||||
foreach (var deviceModelComponent in Database.DeviceComponents.Where(dc => dc.DeviceModelId == dm.Id).ToList())
|
||||
{
|
||||
Database.DeviceComponents.Remove(deviceModelComponent);
|
||||
}
|
||||
|
||||
// Delete Model
|
||||
Database.DeviceModels.Remove(dm);
|
||||
}
|
||||
|
||||
public static Tuple<DeviceModel, bool> GetOrCreateDeviceModel(this DbSet<DeviceModel> DeviceModelsSet, string Manufacturer, string Model, string ModelType)
|
||||
{
|
||||
// Already Exists?
|
||||
var deviceModel = DeviceModelsSet.FirstOrDefault(dm => dm.Manufacturer == Manufacturer && dm.Model == Model);
|
||||
if (deviceModel == null)
|
||||
{
|
||||
// Ensure only one thread/request at a time
|
||||
lock (_CreateDeviceModelLock)
|
||||
{
|
||||
// Check again now that lock is enforced
|
||||
deviceModel = DeviceModelsSet.FirstOrDefault(dm => dm.Manufacturer == Manufacturer && dm.Model == Model);
|
||||
|
||||
if (deviceModel == null)
|
||||
{
|
||||
// Create the Device Model in a different DataContext so we don't have to commit unrelated changes
|
||||
using (DiscoDataContext database = new DiscoDataContext())
|
||||
{
|
||||
var addDeviceModel = new DeviceModel
|
||||
{
|
||||
Manufacturer = Manufacturer,
|
||||
Model = Model,
|
||||
ModelType = ModelType,
|
||||
Description = string.Format("{0} {1}", Manufacturer, Model)
|
||||
};
|
||||
database.DeviceModels.Add(addDeviceModel);
|
||||
database.SaveChanges();
|
||||
}
|
||||
|
||||
// Obtain the Device Model with the in-scope DataContext
|
||||
// - Overhead acknowledged, but reasonable given the infrequency of occurrence
|
||||
deviceModel = DeviceModelsSet.FirstOrDefault(dm => dm.Manufacturer == Manufacturer && dm.Model == Model);
|
||||
return new Tuple<DeviceModel, bool>(deviceModel, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (deviceModel.ModelType != ModelType)
|
||||
deviceModel.ModelType = ModelType;
|
||||
}
|
||||
|
||||
return new Tuple<DeviceModel, bool>(deviceModel, false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.BI.Config;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Devices.ManagedGroups;
|
||||
using Disco.Services.Expressions;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using Disco.Services.Plugins;
|
||||
using Disco.Services.Plugins.Features.CertificateAuthorityProvider;
|
||||
using Disco.Services.Plugins.Features.CertificateProvider;
|
||||
using Disco.Services.Plugins.Features.WirelessProfileProvider;
|
||||
using Disco.Services.Users;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class DeviceProfileExtensions
|
||||
{
|
||||
public const string ComputerNameExpressionCacheModule = "ComputerNameTemplate";
|
||||
|
||||
public static void ComputerNameInvalidateCache(this DeviceProfile deviceProfile)
|
||||
{
|
||||
ExpressionCache.InvalidateKey(ComputerNameExpressionCacheModule, deviceProfile.Id.ToString());
|
||||
}
|
||||
|
||||
public static OrganisationAddress DefaultOrganisationAddressDetails(this DeviceProfile deviceProfile, DiscoDataContext Database)
|
||||
{
|
||||
if (deviceProfile.DefaultOrganisationAddress.HasValue)
|
||||
{
|
||||
return Database.DiscoConfiguration.OrganisationAddresses.GetAddress(deviceProfile.DefaultOrganisationAddress.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CanDelete(this DeviceProfile dp, DiscoDataContext Database)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Config.DeviceProfile.Delete))
|
||||
return false;
|
||||
|
||||
// Can't Delete Default Profile (Id: 1)
|
||||
if (dp.Id == 1)
|
||||
return false;
|
||||
|
||||
// Can't Delete if Contains Devices
|
||||
if (Database.Devices.Count(d => d.DeviceProfileId == dp.Id) > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
public static void Delete(this DeviceProfile dp, DiscoDataContext Database)
|
||||
{
|
||||
if (!dp.CanDelete(Database))
|
||||
throw new InvalidOperationException("The state of this Device Profile doesn't allow it to be deleted");
|
||||
|
||||
// Update Defaults
|
||||
if (Database.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId == dp.Id)
|
||||
Database.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId = 1;
|
||||
if (Database.DiscoConfiguration.DeviceProfiles.DefaultAddDeviceOfflineDeviceProfileId == dp.Id)
|
||||
Database.DiscoConfiguration.DeviceProfiles.DefaultAddDeviceOfflineDeviceProfileId = 1;
|
||||
|
||||
// Remove Linked Group
|
||||
ActiveDirectory.Context.ManagedGroups.Remove(DeviceProfileDevicesManagedGroup.GetKey(dp));
|
||||
ActiveDirectory.Context.ManagedGroups.Remove(DeviceProfileAssignedUsersManagedGroup.GetKey(dp));
|
||||
|
||||
// Delete Profile
|
||||
Database.DeviceProfiles.Remove(dp);
|
||||
}
|
||||
|
||||
public static IEnumerable<PluginFeatureManifest> GetCertificateProviders(this DeviceProfile dp)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(dp.CertificateProviders))
|
||||
{
|
||||
foreach (var certificateProviderId in dp.CertificateProviders.Split(','))
|
||||
{
|
||||
PluginFeatureManifest featureManifest = null;
|
||||
if (Plugins.Plugins.TryGetPluginFeature(certificateProviderId.Trim(), typeof(CertificateProviderFeature), out featureManifest))
|
||||
{
|
||||
yield return featureManifest;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<PluginFeatureManifest> GetCertificateAuthorityProviders(this DeviceProfile dp)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(dp.CertificateAuthorityProviders))
|
||||
{
|
||||
foreach (var certificateAuthorityProviderId in dp.CertificateAuthorityProviders.Split(','))
|
||||
{
|
||||
PluginFeatureManifest featureManifest = null;
|
||||
if (Plugins.Plugins.TryGetPluginFeature(certificateAuthorityProviderId.Trim(), typeof(CertificateAuthorityProviderFeature), out featureManifest))
|
||||
{
|
||||
yield return featureManifest;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<PluginFeatureManifest> GetWirelessProfileProviders(this DeviceProfile dp)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(dp.WirelessProfileProviders))
|
||||
{
|
||||
foreach (var wirelessProfileProviderId in dp.WirelessProfileProviders.Split(','))
|
||||
{
|
||||
PluginFeatureManifest featureManifest = null;
|
||||
if (Plugins.Plugins.TryGetPluginFeature(wirelessProfileProviderId.Trim(), typeof(WirelessProfileProviderFeature), out featureManifest))
|
||||
{
|
||||
yield return featureManifest;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
using Disco.Data.Repository.Monitor;
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Data.Repository.Monitor;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Web.Signalling;
|
||||
using Microsoft.AspNet.SignalR;
|
||||
using Microsoft.AspNet.SignalR.Hubs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reactive.Linq;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Data.Repository;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.Services.Devices
|
||||
{
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.ClientServices;
|
||||
using Disco.Models.ClientServices.EnrolmentInformation;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Plugins.Features.WirelessProfileProvider;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class DeviceWirelessProfileExtensions
|
||||
{
|
||||
|
||||
public static WirelessProfileStore ProvisionWirelessProfiles(this Device device, DiscoDataContext Database, Enrol Enrolment)
|
||||
{
|
||||
var profiles = new List<Models.ClientServices.EnrolmentInformation.WirelessProfile>();
|
||||
var transformations = new List<Models.ClientServices.EnrolmentInformation.WirelessProfileTransformation>();
|
||||
var removeNames = new List<string>();
|
||||
|
||||
foreach (var pluginFeature in device.DeviceProfile.GetWirelessProfileProviders())
|
||||
{
|
||||
using (var providerFeature = pluginFeature.CreateInstance<WirelessProfileProviderFeature>())
|
||||
{
|
||||
var pluginResult = providerFeature.ProvisionWirelessProfiles(Database, device, Enrolment);
|
||||
|
||||
if (pluginResult.Profiles != null)
|
||||
{
|
||||
profiles.AddRange(pluginResult.Profiles.Select(p => new Models.ClientServices.EnrolmentInformation.WirelessProfile()
|
||||
{
|
||||
Name = p.Name,
|
||||
ProfileXml = p.ProfileXml,
|
||||
ForceDeployment = p.ForceDeployment
|
||||
}));
|
||||
}
|
||||
if (pluginResult.Transformations != null)
|
||||
{
|
||||
transformations.AddRange(pluginResult.Transformations.Select(p => new Models.ClientServices.EnrolmentInformation.WirelessProfileTransformation()
|
||||
{
|
||||
Name = p.Name,
|
||||
RegularExpression = p.RegularExpression,
|
||||
RegularExpressionReplacement = p.RegularExpressionReplacement
|
||||
}));
|
||||
}
|
||||
if (pluginResult.RemoveNames != null)
|
||||
{
|
||||
removeNames.AddRange(pluginResult.RemoveNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (profiles.Count == 0 && transformations.Count == 0 && removeNames.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new WirelessProfileStore()
|
||||
{
|
||||
Profiles = profiles.Count > 0 ? profiles : null,
|
||||
Transformations = transformations.Count > 0 ? transformations : null,
|
||||
RemoveNames = removeNames.Count > 0 ? removeNames : null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,647 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.ClientServices;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using Disco.Services.Users;
|
||||
using Exceptionless;
|
||||
using PList;
|
||||
using Renci.SshNet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Disco.Services.Devices.Enrolment
|
||||
{
|
||||
public static class DeviceEnrolment
|
||||
{
|
||||
|
||||
public static MacSecureEnrolResponse MacSecureEnrol(DiscoDataContext Database, string Host)
|
||||
{
|
||||
MacEnrol trustedRequest = new MacEnrol();
|
||||
string sessionId = System.Guid.NewGuid().ToString("B");
|
||||
MacSecureEnrolResponse MacSecureEnrol;
|
||||
try
|
||||
{
|
||||
EnrolmentLog.LogSessionStarting(sessionId, Host, EnrolmentTypes.MacSecure);
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 0, $"Connecting to '{Host}' as '{Database.DiscoConfiguration.Bootstrapper.MacSshUsername}'");
|
||||
|
||||
var sshConnectionInfo = new KeyboardInteractiveConnectionInfo(Host, Database.DiscoConfiguration.Bootstrapper.MacSshUsername);
|
||||
sshConnectionInfo.AuthenticationPrompt += (sender, e) =>
|
||||
{
|
||||
foreach (var prompt in e.Prompts)
|
||||
{
|
||||
if (prompt.Request.StartsWith("Password", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 10, $"Authenticating at '{Host}' as '{Database.DiscoConfiguration.Bootstrapper.MacSshUsername}'");
|
||||
prompt.Response = Database.DiscoConfiguration.Bootstrapper.MacSshPassword;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using (var sshClient = new SshClient(sshConnectionInfo))
|
||||
{
|
||||
sshClient.Connect();
|
||||
|
||||
try
|
||||
{
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 30, "Retrieving System Profile Information");
|
||||
var sshResult = sshClient.RunCommand("system_profiler -xml SPHardwareDataType SPNetworkDataType SPSoftwareDataType");
|
||||
PListRoot profilerData;
|
||||
using (var reader = new StringReader(sshResult.Result))
|
||||
{
|
||||
using (var xmlReader = XmlReader.Create(reader, new XmlReaderSettings() { DtdProcessing = DtdProcessing.Ignore }))
|
||||
{
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(PListRoot));
|
||||
profilerData = (PListRoot)serializer.Deserialize(xmlReader);
|
||||
}
|
||||
}
|
||||
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 90, "Processing System Profile Information");
|
||||
|
||||
PListDict profilerDataHardware = null;
|
||||
PListArray profilerDataNetwork = null;
|
||||
PListDict profilerDataSoftware = null;
|
||||
|
||||
foreach (PListDict node in (profilerData.Root as PListArray))
|
||||
{
|
||||
var nodeItems = ((PListArray)node["_items"]);
|
||||
|
||||
switch (((PListString)node["_dataType"]).Value)
|
||||
{
|
||||
case "SPHardwareDataType":
|
||||
profilerDataHardware = (PListDict)nodeItems[0];
|
||||
break;
|
||||
case "SPNetworkDataType":
|
||||
profilerDataNetwork = nodeItems;
|
||||
break;
|
||||
case "SPSoftwareDataType":
|
||||
profilerDataSoftware = (PListDict)nodeItems[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (profilerDataHardware == null || profilerDataNetwork == null || profilerDataSoftware == null)
|
||||
throw new InvalidOperationException("System Profiler didn't return information for a requested data type");
|
||||
|
||||
trustedRequest.DeviceSerialNumber = (profilerDataHardware["serial_number"] as PListString).Value;
|
||||
trustedRequest.DeviceUUID = (profilerDataHardware["platform_UUID"] as PListString).Value;
|
||||
trustedRequest.DeviceComputerName = (profilerDataSoftware["local_host_name"] as PListString).Value;
|
||||
|
||||
var profilerDataNetworkEthernet = profilerDataNetwork.Cast<PListDict>().FirstOrDefault(e => ((PListString)e["_name"]).Value == "Ethernet");
|
||||
if (profilerDataNetworkEthernet != null)
|
||||
{
|
||||
trustedRequest.DeviceLanMacAddress = ((PListString)(profilerDataNetworkEthernet["Ethernet"] as PListDict)["MAC Address"]).Value;
|
||||
}
|
||||
|
||||
var profilerDataNetworkWiFi = profilerDataNetwork.Cast<PListDict>().FirstOrDefault(e => ((PListString)e["_name"]).Value == "Wi-Fi");
|
||||
if (profilerDataNetworkWiFi != null)
|
||||
{
|
||||
trustedRequest.DeviceWlanMacAddress = ((PListString)(profilerDataNetworkWiFi["Ethernet"] as PListDict)["MAC Address"]).Value;
|
||||
}
|
||||
|
||||
trustedRequest.DeviceManufacturer = "Apple Inc.";
|
||||
trustedRequest.DeviceModel = (profilerDataHardware["machine_model"] as PListString).Value;
|
||||
|
||||
trustedRequest.DeviceModelType = ParseMacModelType((profilerDataHardware["machine_name"] as PListString).Value);
|
||||
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 99, "Disconnecting");
|
||||
|
||||
sshClient.Disconnect();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (sshClient != null)
|
||||
{
|
||||
bool connected = sshClient.IsConnected;
|
||||
if (connected)
|
||||
{
|
||||
sshClient.Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 100, "Disconnected, Starting Disco Enrolment");
|
||||
MacSecureEnrolResponse response = MacSecureEnrolResponse.FromMacEnrolResponse(MacEnrol(Database, trustedRequest, true, sessionId));
|
||||
EnrolmentLog.LogSessionFinished(sessionId);
|
||||
MacSecureEnrol = response;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
ex.ToExceptionless().Submit();
|
||||
EnrolmentLog.LogSessionError(sessionId, ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
return MacSecureEnrol;
|
||||
}
|
||||
|
||||
#region "Mac Enrol Helpers"
|
||||
|
||||
private static string ParseMacModelType(string ModelName)
|
||||
{
|
||||
string ParseMacModelType;
|
||||
if (!string.IsNullOrWhiteSpace(ModelName))
|
||||
{
|
||||
string mn = ModelName.ToLower();
|
||||
if (mn.Contains("imac") || mn.Contains("mini"))
|
||||
{
|
||||
ParseMacModelType = "Desktop";
|
||||
return ParseMacModelType;
|
||||
}
|
||||
if (mn.Contains("macbook"))
|
||||
{
|
||||
ParseMacModelType = "Mobile";
|
||||
return ParseMacModelType;
|
||||
}
|
||||
if (mn.Contains("xserve"))
|
||||
{
|
||||
ParseMacModelType = "Server";
|
||||
return ParseMacModelType;
|
||||
}
|
||||
}
|
||||
ParseMacModelType = "Unknown";
|
||||
return ParseMacModelType;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static MacEnrolResponse MacEnrol(DiscoDataContext Database, MacEnrol Request, bool Trusted, string OpenSessionId = null)
|
||||
{
|
||||
string sessionId;
|
||||
if (OpenSessionId == null)
|
||||
{
|
||||
sessionId = System.Guid.NewGuid().ToString("B");
|
||||
EnrolmentLog.LogSessionStarting(sessionId, Request.DeviceSerialNumber, EnrolmentTypes.Mac);
|
||||
}
|
||||
else
|
||||
{
|
||||
sessionId = OpenSessionId;
|
||||
}
|
||||
EnrolmentLog.LogSessionDeviceInfo(sessionId, Request);
|
||||
MacEnrolResponse response = new MacEnrolResponse();
|
||||
try
|
||||
{
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 10, "Querying Database");
|
||||
Device RepoDevice = Database.Devices.Include("AssignedUser").Include("DeviceProfile").Include("DeviceProfile").Where(d => d.SerialNumber == Request.DeviceSerialNumber).FirstOrDefault();
|
||||
if (!Trusted)
|
||||
{
|
||||
if (RepoDevice == null)
|
||||
throw new EnrolmentSafeException(string.Format("Unknown Device Serial Number (SN: '{0}')", Request.DeviceSerialNumber));
|
||||
if (!RepoDevice.AllowUnauthenticatedEnrol)
|
||||
throw new EnrolmentSafeException(string.Format("Device isn't allowed an Unauthenticated Enrolment (SN: '{0}')", Request.DeviceSerialNumber));
|
||||
}
|
||||
if (RepoDevice == null)
|
||||
{
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 50, "New Device, Building Disco Instance");
|
||||
EnrolmentLog.LogSessionTaskAddedDevice(sessionId, Request.DeviceSerialNumber);
|
||||
DeviceProfile deviceProfile = Database.DeviceProfiles.Find(Database.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId);
|
||||
|
||||
var deviceModelResult = Database.DeviceModels.GetOrCreateDeviceModel(Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim(), Request.DeviceModelType.Trim());
|
||||
DeviceModel deviceModel = deviceModelResult.Item1;
|
||||
if (deviceModelResult.Item2)
|
||||
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.DeviceSerialNumber, deviceModelResult.Item1.Manufacturer, deviceModelResult.Item1.Model);
|
||||
else
|
||||
EnrolmentLog.LogSessionDevice(sessionId, Request.DeviceSerialNumber, deviceModel.Id);
|
||||
|
||||
RepoDevice = new Device
|
||||
{
|
||||
SerialNumber = Request.DeviceSerialNumber,
|
||||
DeviceDomainId = Request.DeviceComputerName,
|
||||
DeviceProfile = deviceProfile,
|
||||
DeviceModel = deviceModel,
|
||||
AllowUnauthenticatedEnrol = false,
|
||||
CreatedDate = DateTime.Now,
|
||||
EnrolledDate = DateTime.Now
|
||||
};
|
||||
Database.Devices.Add(RepoDevice);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 50, "Existing Device, Updating Disco Instance");
|
||||
EnrolmentLog.LogSessionTaskUpdatingDevice(sessionId, Request.DeviceSerialNumber);
|
||||
|
||||
var deviceModelResult = Database.DeviceModels.GetOrCreateDeviceModel(Request.DeviceManufacturer.Trim(), Request.DeviceModel.Trim(), Request.DeviceModelType.Trim());
|
||||
DeviceModel deviceModel = deviceModelResult.Item1;
|
||||
if (deviceModelResult.Item2)
|
||||
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.DeviceSerialNumber, deviceModelResult.Item1.Manufacturer, deviceModelResult.Item1.Model);
|
||||
else
|
||||
EnrolmentLog.LogSessionDevice(sessionId, Request.DeviceSerialNumber, deviceModel.Id);
|
||||
|
||||
RepoDevice.DeviceModel = deviceModel;
|
||||
|
||||
RepoDevice.DeviceDomainId = Request.DeviceComputerName;
|
||||
if (!RepoDevice.EnrolledDate.HasValue)
|
||||
{
|
||||
RepoDevice.EnrolledDate = DateTime.Now;
|
||||
}
|
||||
}
|
||||
RepoDevice.LastEnrolDate = DateTime.Now;
|
||||
RepoDevice.AllowUnauthenticatedEnrol = false;
|
||||
// Removed 2012-06-14 G# - Properties moved to DeviceProfile model & DB Migrated in DBv3.
|
||||
//DeviceProfileConfiguration RepoDeviceProfileContext = RepoDevice.DeviceProfile.Configuration(Context);
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 90, "Building Response");
|
||||
//if (RepoDeviceProfileContext.DistributionType == DeviceProfileConfiguration.DeviceProfileDistributionTypes.OneToOne && RepoDevice.AssignedUser != null)
|
||||
if (RepoDevice.DeviceProfile.DistributionType == DeviceProfile.DistributionTypes.OneToOne && RepoDevice.AssignedUser != null)
|
||||
{
|
||||
ADUserAccount AssignedUserInfo = ActiveDirectory.RetrieveADUserAccount(RepoDevice.AssignedUser.UserId);
|
||||
EnrolmentLog.LogSessionTaskAssigningUser(sessionId, RepoDevice.SerialNumber, AssignedUserInfo.DisplayName, AssignedUserInfo.SamAccountName, AssignedUserInfo.Domain.NetBiosName, AssignedUserInfo.SecurityIdentifier.ToString());
|
||||
response.DeviceAssignedUserUsername = AssignedUserInfo.SamAccountName;
|
||||
response.DeviceAssignedUserDomain = AssignedUserInfo.Domain.NetBiosName;
|
||||
response.DeviceAssignedUserName = AssignedUserInfo.DisplayName;
|
||||
response.DeviceAssignedUserSID = AssignedUserInfo.SecurityIdentifier.ToString();
|
||||
}
|
||||
response.DeviceComputerName = RepoDevice.DeviceDomainId;
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 100, "Completed Successfully");
|
||||
}
|
||||
catch (EnrolmentSafeException ex)
|
||||
{
|
||||
EnrolmentLog.LogSessionError(sessionId, ex);
|
||||
return new MacEnrolResponse { ErrorMessage = ex.Message };
|
||||
}
|
||||
catch (System.Exception ex2)
|
||||
{
|
||||
ex2.ToExceptionless().Submit();
|
||||
EnrolmentLog.LogSessionError(sessionId, ex2);
|
||||
throw ex2;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (OpenSessionId == null)
|
||||
EnrolmentLog.LogSessionFinished(sessionId);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
public static EnrolResponse Enrol(DiscoDataContext Database, string Username, Enrol Request)
|
||||
{
|
||||
ADMachineAccount adMachineAccount = null;
|
||||
|
||||
EnrolResponse response = new EnrolResponse();
|
||||
|
||||
AuthorizationToken authenticatedToken = null;
|
||||
bool isAuthenticated = false;
|
||||
|
||||
ADDomain domain = null;
|
||||
Lazy<ADDomainController> domainController = new Lazy<ADDomainController>(() =>
|
||||
{
|
||||
if (domain == null)
|
||||
throw new InvalidOperationException("The [domain] variable must be initialized first");
|
||||
return domain.GetAvailableDomainController(RequireWritable: true);
|
||||
});
|
||||
|
||||
string sessionId = System.Guid.NewGuid().ToString("B");
|
||||
response.SessionId = sessionId;
|
||||
|
||||
EnrolmentLog.LogSessionStarting(sessionId, Request.SerialNumber, EnrolmentTypes.Normal);
|
||||
EnrolmentLog.LogSessionDeviceInfo(sessionId, Request);
|
||||
|
||||
try
|
||||
{
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 10, "Loading User Data");
|
||||
if (!string.IsNullOrWhiteSpace(Username))
|
||||
{
|
||||
authenticatedToken = UserService.GetAuthorization(Username, Database);
|
||||
isAuthenticated = (authenticatedToken != null);
|
||||
}
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 13, "Loading Device Data");
|
||||
|
||||
Device RepoDevice = Database.Devices.Include("AssignedUser").Include("DeviceModel").Include("DeviceProfile").Where(d => d.SerialNumber == Request.SerialNumber).FirstOrDefault();
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 15, "Discovering User/Device Disco Permissions");
|
||||
if (isAuthenticated)
|
||||
{
|
||||
if (!authenticatedToken.Has(Claims.Device.Actions.EnrolDevices))
|
||||
{
|
||||
if (!authenticatedToken.Has(Claims.ComputerAccount))
|
||||
throw new EnrolmentSafeException(string.Format("Connection not correctly authenticated (SN: {0}; Auth User: {1})", Request.SerialNumber, authenticatedToken.User.UserId));
|
||||
|
||||
if (domain == null)
|
||||
domain = ActiveDirectory.Context.GetDomainByName(Request.DNSDomainName);
|
||||
|
||||
if (!authenticatedToken.User.UserId.Equals(string.Format(@"{0}\{1}$", domain.NetBiosName, Request.ComputerName), System.StringComparison.OrdinalIgnoreCase))
|
||||
throw new EnrolmentSafeException(string.Format("Connection not correctly authenticated (SN: {0}; Auth User: {1})", Request.SerialNumber, authenticatedToken.User.UserId));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (RepoDevice == null)
|
||||
{
|
||||
throw new EnrolmentSafeException(string.Format("Unknown Device Serial Number (SN: '{0}')", Request.SerialNumber));
|
||||
}
|
||||
if (!RepoDevice.AllowUnauthenticatedEnrol)
|
||||
{
|
||||
if (RepoDevice.DeviceProfile.AllowUntrustedReimageJobEnrolment)
|
||||
{
|
||||
if (Database.Jobs.Count(j => j.DeviceSerialNumber == RepoDevice.SerialNumber && j.JobTypeId == JobType.JobTypeIds.SImg && !j.ClosedDate.HasValue) == 0)
|
||||
{
|
||||
throw new EnrolmentSafeException(string.Format("Device has no open 'Software - Reimage' job (SN: '{0}')", Request.SerialNumber));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new EnrolmentSafeException(string.Format("Device isn't allowed an Unauthenticated Enrolment (SN: '{0}')", Request.SerialNumber));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Request.IsPartOfDomain && !string.IsNullOrWhiteSpace(Request.ComputerName))
|
||||
{
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 20, "Loading Active Directory Computer Account");
|
||||
System.Guid? uuidGuid = null;
|
||||
System.Guid? macAddressGuid = null;
|
||||
if (!string.IsNullOrEmpty(Request.Hardware.UUID))
|
||||
uuidGuid = ADMachineAccount.NetbootGUIDFromUUID(Request.Hardware.UUID);
|
||||
|
||||
// Use non-Wlan Adapter with fastest speed
|
||||
var macAddress = Request.Hardware?.NetworkAdapters?.Where(na => !na.IsWlanAdapter).OrderByDescending(na => na.Speed).Select(na => na.MACAddress).FirstOrDefault();
|
||||
if (!string.IsNullOrEmpty(macAddress))
|
||||
macAddressGuid = ADMachineAccount.NetbootGUIDFromMACAddress(macAddress);
|
||||
|
||||
if (domain == null)
|
||||
domain = ActiveDirectory.Context.GetDomainByName(Request.DNSDomainName);
|
||||
|
||||
var requestDeviceId = string.Format(@"{0}\{1}", domain.NetBiosName, Request.ComputerName);
|
||||
|
||||
adMachineAccount = domainController.Value.RetrieveADMachineAccount(requestDeviceId, uuidGuid, macAddressGuid);
|
||||
}
|
||||
if (RepoDevice == null)
|
||||
{
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 30, "New Device, Creating Disco Instance");
|
||||
EnrolmentLog.LogSessionTaskAddedDevice(sessionId, Request.SerialNumber);
|
||||
DeviceProfile deviceProfile = Database.DeviceProfiles.Find(Database.DiscoConfiguration.DeviceProfiles.DefaultDeviceProfileId);
|
||||
|
||||
|
||||
var deviceModelResult = Database.DeviceModels.GetOrCreateDeviceModel(Request.Hardware.Manufacturer.Trim(), Request.Hardware.Model.Trim(), Request.Hardware.ModelType.Trim());
|
||||
DeviceModel deviceModel = deviceModelResult.Item1;
|
||||
if (deviceModelResult.Item2)
|
||||
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.SerialNumber, deviceModelResult.Item1.Manufacturer, deviceModelResult.Item1.Model);
|
||||
else
|
||||
EnrolmentLog.LogSessionDevice(sessionId, Request.SerialNumber, deviceModel.Id);
|
||||
|
||||
if (domain == null)
|
||||
domain = ActiveDirectory.Context.GetDomainByName(Request.DNSDomainName);
|
||||
|
||||
RepoDevice = new Device
|
||||
{
|
||||
SerialNumber = Request.SerialNumber,
|
||||
DeviceDomainId = string.Format(@"{0}\{1}", domain.NetBiosName, Request.ComputerName),
|
||||
DeviceProfile = deviceProfile,
|
||||
DeviceModel = deviceModel,
|
||||
AllowUnauthenticatedEnrol = false,
|
||||
CreatedDate = DateTime.Now,
|
||||
EnrolledDate = DateTime.Now,
|
||||
LastEnrolDate = DateTime.Now,
|
||||
DeviceDetails = new List<DeviceDetail>()
|
||||
};
|
||||
Database.Devices.Add(RepoDevice);
|
||||
|
||||
var lanMacAddresses = string.Join("; ", Request.Hardware.NetworkAdapters?.Where(na => !na.IsWlanAdapter).Select(na => na.MACAddress));
|
||||
var wlanMacAddresses = string.Join("; ", Request.Hardware.NetworkAdapters?.Where(na => na.IsWlanAdapter).Select(na => na.MACAddress));
|
||||
if (!string.IsNullOrEmpty(lanMacAddresses))
|
||||
RepoDevice.DeviceDetails.LanMacAddress(RepoDevice, lanMacAddresses);
|
||||
if (!string.IsNullOrEmpty(wlanMacAddresses))
|
||||
RepoDevice.DeviceDetails.WLanMacAddress(RepoDevice, wlanMacAddresses);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 30, "Existing Device, Updating Disco Instance");
|
||||
EnrolmentLog.LogSessionTaskUpdatingDevice(sessionId, Request.SerialNumber);
|
||||
|
||||
var deviceModelResult = Database.DeviceModels.GetOrCreateDeviceModel(Request.Hardware.Manufacturer.Trim(), Request.Hardware.Model.Trim(), Request.Hardware.ModelType.Trim());
|
||||
DeviceModel deviceModel = deviceModelResult.Item1;
|
||||
if (deviceModelResult.Item2)
|
||||
EnrolmentLog.LogSessionTaskCreatedDeviceModel(sessionId, Request.SerialNumber, deviceModelResult.Item1.Manufacturer, deviceModelResult.Item1.Model);
|
||||
else
|
||||
EnrolmentLog.LogSessionDevice(sessionId, Request.SerialNumber, deviceModel.Id);
|
||||
|
||||
RepoDevice.DeviceModel = deviceModel;
|
||||
|
||||
var lanMacAddresses = string.Join("; ", Request.Hardware.NetworkAdapters?.Where(na => !na.IsWlanAdapter).Select(na => na.MACAddress));
|
||||
var wlanMacAddresses = string.Join("; ", Request.Hardware.NetworkAdapters?.Where(na => na.IsWlanAdapter).Select(na => na.MACAddress));
|
||||
if (!string.IsNullOrEmpty(lanMacAddresses))
|
||||
RepoDevice.DeviceDetails.LanMacAddress(RepoDevice, lanMacAddresses);
|
||||
if (!string.IsNullOrEmpty(wlanMacAddresses))
|
||||
RepoDevice.DeviceDetails.WLanMacAddress(RepoDevice, wlanMacAddresses);
|
||||
|
||||
if (!RepoDevice.EnrolledDate.HasValue)
|
||||
RepoDevice.EnrolledDate = DateTime.Now;
|
||||
RepoDevice.LastEnrolDate = DateTime.Now;
|
||||
}
|
||||
|
||||
if (adMachineAccount == null)
|
||||
{
|
||||
if (RepoDevice.DeviceProfile.ProvisionADAccount)
|
||||
{
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 50, "Provisioning an Active Directory Computer Account");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(RepoDevice.DeviceProfile.OrganisationalUnit))
|
||||
throw new InvalidOperationException("No Organisational Unit has been set in the device profile");
|
||||
if (domain == null)
|
||||
domain = ActiveDirectory.Context.GetDomainFromDistinguishedName(RepoDevice.DeviceProfile.OrganisationalUnit);
|
||||
|
||||
if (string.IsNullOrEmpty(RepoDevice.DeviceDomainId) || RepoDevice.DeviceProfile.EnforceComputerNameConvention)
|
||||
RepoDevice.DeviceDomainId = RepoDevice.ComputerNameRender(Database, domain);
|
||||
|
||||
string offlineProvisionDiagnosicInfo;
|
||||
EnrolmentLog.LogSessionTaskProvisioningADAccount(sessionId, RepoDevice.SerialNumber, RepoDevice.DeviceDomainId);
|
||||
adMachineAccount = domainController.Value.RetrieveADMachineAccount(RepoDevice.DeviceDomainId);
|
||||
|
||||
response.OfflineDomainJoinManifest = domainController.Value.OfflineDomainJoinProvision(RepoDevice.DeviceDomainId, RepoDevice.DeviceProfile.OrganisationalUnit, ref adMachineAccount, out offlineProvisionDiagnosicInfo);
|
||||
|
||||
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, offlineProvisionDiagnosicInfo);
|
||||
|
||||
response.RequireReboot = true;
|
||||
}
|
||||
if (adMachineAccount != null)
|
||||
{
|
||||
response.ComputerName = adMachineAccount.Name;
|
||||
response.DomainName = adMachineAccount.Domain.NetBiosName;
|
||||
}
|
||||
else if (ActiveDirectory.IsValidDomainAccountId(RepoDevice.DeviceDomainId))
|
||||
{
|
||||
string accountUsername;
|
||||
ADDomain accountDomain;
|
||||
ActiveDirectory.ParseDomainAccountId(RepoDevice.DeviceDomainId, out accountUsername, out accountDomain);
|
||||
|
||||
response.DomainName = accountDomain == null ? null : accountDomain.NetBiosName;
|
||||
response.ComputerName = accountUsername;
|
||||
}
|
||||
else
|
||||
{
|
||||
response.DomainName = Request.DNSDomainName;
|
||||
response.ComputerName = Request.ComputerName;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RepoDevice.DeviceDomainId = adMachineAccount.Id.Trim('$');
|
||||
response.ComputerName = adMachineAccount.Name;
|
||||
response.DomainName = adMachineAccount.Domain.NetBiosName;
|
||||
|
||||
// Enforce Computer Name Convention
|
||||
if (!adMachineAccount.IsCriticalSystemObject && RepoDevice.DeviceProfile.EnforceComputerNameConvention)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(RepoDevice.DeviceProfile.OrganisationalUnit))
|
||||
throw new InvalidOperationException("No Organisational Unit has been set in the device profile");
|
||||
if (domain == null)
|
||||
domain = ActiveDirectory.Context.GetDomainFromDistinguishedName(RepoDevice.DeviceProfile.OrganisationalUnit);
|
||||
|
||||
var calculatedComputerName = RepoDevice.ComputerNameRender(Database, domain);
|
||||
string calculatedAccountUsername;
|
||||
ActiveDirectory.ParseDomainAccountId(calculatedComputerName, out calculatedAccountUsername);
|
||||
|
||||
if (!Request.ComputerName.Equals(calculatedAccountUsername, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 50, string.Format("Renaming Device: {0} -> {1}", Request.ComputerName, calculatedComputerName));
|
||||
EnrolmentLog.LogSessionTaskRenamingDevice(sessionId, Request.ComputerName, calculatedComputerName);
|
||||
|
||||
RepoDevice.DeviceDomainId = calculatedComputerName;
|
||||
response.DomainName = domain.NetBiosName;
|
||||
response.ComputerName = calculatedAccountUsername;
|
||||
|
||||
// Create New Account
|
||||
string offlineProvisionDiagnosicInfo;
|
||||
|
||||
response.OfflineDomainJoinManifest = domainController.Value.OfflineDomainJoinProvision(RepoDevice.DeviceDomainId, RepoDevice.DeviceProfile.OrganisationalUnit, ref adMachineAccount, out offlineProvisionDiagnosicInfo);
|
||||
|
||||
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, offlineProvisionDiagnosicInfo);
|
||||
|
||||
response.RequireReboot = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce Organisational Unit
|
||||
if (!adMachineAccount.IsCriticalSystemObject && response.OfflineDomainJoinManifest == null && RepoDevice.DeviceProfile.EnforceOrganisationalUnit)
|
||||
{
|
||||
var parentDistinguishedName = adMachineAccount.ParentDistinguishedName;
|
||||
if (string.IsNullOrWhiteSpace(RepoDevice.DeviceProfile.OrganisationalUnit))
|
||||
throw new InvalidOperationException(string.Format("The Organisational Unit for the Device Profile '{0}' [{1}] is not set.", RepoDevice.DeviceProfile.Name, RepoDevice.DeviceProfile.Id));
|
||||
|
||||
if (!parentDistinguishedName.Equals(RepoDevice.DeviceProfile.OrganisationalUnit, StringComparison.OrdinalIgnoreCase)) // Custom OU
|
||||
{
|
||||
var proposedDomain = ActiveDirectory.Context.GetDomainFromDistinguishedName(RepoDevice.DeviceProfile.OrganisationalUnit);
|
||||
var currentDomain = ActiveDirectory.Context.GetDomainFromDistinguishedName(parentDistinguishedName);
|
||||
if (currentDomain != proposedDomain)
|
||||
throw new NotSupportedException("Unable to move the devices organisational unit when the source and destination domains are different.");
|
||||
if (domain == null)
|
||||
domain = proposedDomain;
|
||||
else if (domain != proposedDomain)
|
||||
throw new NotSupportedException("To many domains involved in this enrolment, contact support regarding your scenario.");
|
||||
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 65, string.Format("Moving Device Organisational Unit: {0} -> {1}", parentDistinguishedName, RepoDevice.DeviceProfile.OrganisationalUnit));
|
||||
EnrolmentLog.LogSessionTaskMovingDeviceOrganisationUnit(sessionId, parentDistinguishedName, RepoDevice.DeviceProfile.OrganisationalUnit);
|
||||
adMachineAccount.MoveOrganisationalUnit(domainController.Value, RepoDevice.DeviceProfile.OrganisationalUnit);
|
||||
response.RequireReboot = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (adMachineAccount != null && !adMachineAccount.IsCriticalSystemObject)
|
||||
{
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 75, "Updating Active Directory Computer Account Properties");
|
||||
// Use non-Wlan Adapter with fastest speed
|
||||
var macAddress = Request.Hardware?.NetworkAdapters?.Where(na => !na.IsWlanAdapter).OrderByDescending(na => na.Speed).Select(na => na.MACAddress).FirstOrDefault();
|
||||
adMachineAccount.UpdateNetbootGUID(Request.Hardware.UUID, macAddress);
|
||||
if (RepoDevice.AssignedUser != null)
|
||||
adMachineAccount.SetDescription(RepoDevice);
|
||||
}
|
||||
if (RepoDevice.DeviceProfile.DistributionType == DeviceProfile.DistributionTypes.OneToOne)
|
||||
{
|
||||
if (RepoDevice.AssignedUser == null)
|
||||
{
|
||||
response.AllowBootstrapperUninstall = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 80, "Retrieving Active Directory Assigned User Account");
|
||||
ADUserAccount AssignedUserInfo = ActiveDirectory.RetrieveADUserAccount(RepoDevice.AssignedUser.UserId);
|
||||
EnrolmentLog.LogSessionTaskAssigningUser(sessionId, RepoDevice.SerialNumber, AssignedUserInfo.DisplayName, AssignedUserInfo.SamAccountName, AssignedUserInfo.Domain.NetBiosName, AssignedUserInfo.SecurityIdentifier.ToString());
|
||||
response.AllowBootstrapperUninstall = true;
|
||||
response.AssignedUserIsLocalAdmin = RepoDevice.DeviceProfile.AssignedUserLocalAdmin;
|
||||
response.AssignedUserUsername = AssignedUserInfo.SamAccountName;
|
||||
response.AssignedUserDomain = AssignedUserInfo.Domain.NetBiosName;
|
||||
response.AssignedUserDescription = AssignedUserInfo.DisplayName;
|
||||
response.AssignedUserSID = AssignedUserInfo.SecurityIdentifier.ToString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
response.AllowBootstrapperUninstall = true;
|
||||
}
|
||||
|
||||
// Provision Certificates
|
||||
if (!string.IsNullOrEmpty(RepoDevice.DeviceProfile.CertificateProviders) ||
|
||||
!string.IsNullOrEmpty(RepoDevice.DeviceProfile.CertificateAuthorityProviders))
|
||||
{
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 90, "Provisioning Certificates");
|
||||
|
||||
List<DeviceCertificate> provisionedCertificates;
|
||||
var provisionResult = RepoDevice.ProvisionCertificates(Database, Request, out provisionedCertificates);
|
||||
|
||||
if (provisionedCertificates != null && provisionedCertificates.Count > 0)
|
||||
{
|
||||
foreach (var deviceCertificate in provisionedCertificates)
|
||||
{
|
||||
EnrolmentLog.LogSessionTaskProvisioningCertificate(sessionId, RepoDevice.SerialNumber, deviceCertificate.Name);
|
||||
}
|
||||
}
|
||||
|
||||
response.Certificates = provisionResult;
|
||||
}
|
||||
|
||||
// Provision Wireless Profiles
|
||||
if (!string.IsNullOrEmpty(RepoDevice.DeviceProfile.WirelessProfileProviders))
|
||||
{
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 95, "Provisioning Wireless Profiles");
|
||||
|
||||
var provisionResult = RepoDevice.ProvisionWirelessProfiles(Database, Request);
|
||||
|
||||
if (provisionResult != null && provisionResult.Profiles != null)
|
||||
{
|
||||
foreach (var wirelessProfiles in provisionResult.Profiles)
|
||||
{
|
||||
EnrolmentLog.LogSessionTaskProvisioningCertificate(sessionId, RepoDevice.SerialNumber, wirelessProfiles.Name);
|
||||
}
|
||||
}
|
||||
|
||||
response.WirelessProfiles = provisionResult;
|
||||
}
|
||||
|
||||
// Reset 'AllowUnauthenticatedEnrol'
|
||||
if (RepoDevice.AllowUnauthenticatedEnrol)
|
||||
RepoDevice.AllowUnauthenticatedEnrol = false;
|
||||
|
||||
EnrolmentLog.LogSessionProgress(sessionId, 100, "Completed Successfully");
|
||||
}
|
||||
catch (EnrolmentSafeException ex)
|
||||
{
|
||||
EnrolmentLog.LogSessionError(sessionId, ex);
|
||||
return new EnrolResponse
|
||||
{
|
||||
SessionId = sessionId,
|
||||
ErrorMessage = ex.Message
|
||||
};
|
||||
}
|
||||
catch (System.Exception ex2)
|
||||
{
|
||||
ex2.ToExceptionless().Submit();
|
||||
EnrolmentLog.LogSessionError(sessionId, ex2);
|
||||
throw ex2;
|
||||
}
|
||||
finally
|
||||
{
|
||||
EnrolmentLog.LogSessionFinished(sessionId);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,503 @@
|
||||
using Disco.Models.ClientServices;
|
||||
using Disco.Services.Logging;
|
||||
using Disco.Services.Logging.Models;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Disco.Services.Devices.Enrolment
|
||||
{
|
||||
public class EnrolmentLog : LogBase
|
||||
{
|
||||
public enum EventTypeIds
|
||||
{
|
||||
SessionStarting = 10,
|
||||
SessionProgress,
|
||||
SessionDevice,
|
||||
SessionDeviceInfo,
|
||||
SessionFinished = 20,
|
||||
SessionDiagnosticInformation,
|
||||
SessionWarning,
|
||||
SessionError,
|
||||
SessionErrorWithInner,
|
||||
SessionClientError,
|
||||
SessionTaskAddedDevice = 50,
|
||||
SessionTaskUpdatingDevice,
|
||||
SessionTaskCreatedDeviceModel = 56,
|
||||
SessionTaskProvisioningADAccount = 58,
|
||||
SessionTaskAssigningUser = 60,
|
||||
SessionTaskProvisioningCertificate = 62,
|
||||
SessionTaskProvisioningWirelessProfile = 63,
|
||||
SessionTaskRenamingDevice = 64,
|
||||
SessionTaskMovingDeviceOrganisationUnit = 66,
|
||||
ClientError = 400
|
||||
}
|
||||
private const int _ModuleId = 50;
|
||||
public static EnrolmentLog Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return (EnrolmentLog)LogContext.LogModules[50];
|
||||
}
|
||||
}
|
||||
public override string ModuleDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Device Enrolment";
|
||||
}
|
||||
}
|
||||
public override int ModuleId
|
||||
{
|
||||
get
|
||||
{
|
||||
return 50;
|
||||
}
|
||||
}
|
||||
public override string ModuleName
|
||||
{
|
||||
get
|
||||
{
|
||||
return "DeviceEnrolment";
|
||||
}
|
||||
}
|
||||
[System.Diagnostics.DebuggerNonUserCode]
|
||||
public EnrolmentLog()
|
||||
{
|
||||
}
|
||||
private static void Log(EnrolmentLog.EventTypeIds EventTypeId, params object[] Args)
|
||||
{
|
||||
EnrolmentLog.Current.Log((int)EventTypeId, Args);
|
||||
}
|
||||
public static void LogSessionStarting(string SessionId, string HostId, EnrolmentTypes EnrolmentType)
|
||||
{
|
||||
Log(EventTypeIds.SessionStarting, new object[]
|
||||
{
|
||||
SessionId,
|
||||
HostId,
|
||||
System.Enum.GetName(EnrolmentType.GetType(), EnrolmentType)
|
||||
});
|
||||
}
|
||||
public static void LogSessionDevice(string SessionId, string DeviceSerialNumber, int? DeviceModelId)
|
||||
{
|
||||
Log(EventTypeIds.SessionDevice, new object[]
|
||||
{
|
||||
SessionId,
|
||||
DeviceSerialNumber,
|
||||
DeviceModelId
|
||||
});
|
||||
}
|
||||
public static void LogSessionDeviceInfo(string SessionId, string SerialNumber, string UUID, string ComputerName, string LanMacAddress, string WlanMacAddress, string Manufacturer, string Model, string ModelType)
|
||||
{
|
||||
Log(EventTypeIds.SessionDeviceInfo, new object[]
|
||||
{
|
||||
SessionId,
|
||||
SerialNumber,
|
||||
UUID,
|
||||
ComputerName,
|
||||
LanMacAddress,
|
||||
WlanMacAddress,
|
||||
Manufacturer,
|
||||
Model,
|
||||
ModelType
|
||||
});
|
||||
}
|
||||
public static void LogSessionDeviceInfo(string SessionId, MacEnrol Request)
|
||||
{
|
||||
EnrolmentLog.LogSessionDeviceInfo(SessionId, Request.DeviceSerialNumber, Request.DeviceUUID, Request.DeviceComputerName, Request.DeviceLanMacAddress, Request.DeviceWlanMacAddress, Request.DeviceManufacturer, Request.DeviceModel, Request.DeviceModelType);
|
||||
}
|
||||
public static void LogSessionDeviceInfo(string SessionId, Enrol Request)
|
||||
{
|
||||
EnrolmentLog.LogSessionDeviceInfo(SessionId, Request.SerialNumber, Request.Hardware.UUID, Request.ComputerName, null, null, Request.Hardware.Manufacturer, Request.Hardware.Model, Request.Hardware.ModelType);
|
||||
}
|
||||
|
||||
public static void LogSessionProgress(string SessionId, int Progress, string Status)
|
||||
{
|
||||
Log(EventTypeIds.SessionProgress, new object[]
|
||||
{
|
||||
SessionId,
|
||||
Progress,
|
||||
Status
|
||||
});
|
||||
}
|
||||
public static void LogSessionFinished(string SessionId)
|
||||
{
|
||||
Log(EventTypeIds.SessionFinished, new object[]
|
||||
{
|
||||
SessionId
|
||||
});
|
||||
}
|
||||
public static void LogSessionDiagnosticInformation(string SessionId, string Message)
|
||||
{
|
||||
Log(EventTypeIds.SessionDiagnosticInformation, new object[]
|
||||
{
|
||||
SessionId,
|
||||
Message
|
||||
});
|
||||
}
|
||||
public static void LogSessionWarning(string SessionId, string Message)
|
||||
{
|
||||
Log(EventTypeIds.SessionWarning, new object[]
|
||||
{
|
||||
SessionId,
|
||||
Message
|
||||
});
|
||||
}
|
||||
public static void LogSessionError(string SessionId, System.Exception Ex)
|
||||
{
|
||||
if (Ex.InnerException == null)
|
||||
{
|
||||
Log(EventTypeIds.SessionError, new object[]
|
||||
{
|
||||
SessionId,
|
||||
Ex.GetType().Name,
|
||||
Ex.Message,
|
||||
Ex.StackTrace
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(EventTypeIds.SessionErrorWithInner, new object[]
|
||||
{
|
||||
SessionId,
|
||||
Ex.GetType().Name,
|
||||
Ex.Message,
|
||||
Ex.InnerException.GetType().Name,
|
||||
Ex.InnerException.Message,
|
||||
Ex.StackTrace,
|
||||
Ex.InnerException.StackTrace
|
||||
});
|
||||
}
|
||||
}
|
||||
public static void LogSessionClientError(string SessionId, string ClientIP, string ClientIdentifier, string ClientVersion, string Error, string RawError)
|
||||
{
|
||||
Log(EventTypeIds.SessionClientError, new object[]
|
||||
{
|
||||
SessionId,
|
||||
ClientIP,
|
||||
ClientIdentifier,
|
||||
ClientVersion,
|
||||
Error,
|
||||
RawError
|
||||
});
|
||||
}
|
||||
public static void LogSessionTaskAddedDevice(string SessionId, string DeviceSerialNumber)
|
||||
{
|
||||
Log(EventTypeIds.SessionTaskAddedDevice, new object[]
|
||||
{
|
||||
SessionId,
|
||||
DeviceSerialNumber
|
||||
});
|
||||
}
|
||||
public static void LogSessionTaskUpdatingDevice(string SessionId, string DeviceSerialNumber)
|
||||
{
|
||||
Log(EventTypeIds.SessionTaskUpdatingDevice, new object[]
|
||||
{
|
||||
SessionId,
|
||||
DeviceSerialNumber
|
||||
});
|
||||
}
|
||||
public static void LogSessionTaskCreatedDeviceModel(string SessionId, string DeviceSerialNumber, string Manufacturer, string Model)
|
||||
{
|
||||
Log(EventTypeIds.SessionTaskCreatedDeviceModel, new object[]
|
||||
{
|
||||
SessionId,
|
||||
DeviceSerialNumber,
|
||||
Manufacturer,
|
||||
Model
|
||||
});
|
||||
}
|
||||
public static void LogSessionTaskProvisioningADAccount(string SessionId, string DeviceSerialNumber, string ADAccountName)
|
||||
{
|
||||
Log(EventTypeIds.SessionTaskProvisioningADAccount, new object[]
|
||||
{
|
||||
SessionId,
|
||||
DeviceSerialNumber,
|
||||
ADAccountName
|
||||
});
|
||||
}
|
||||
public static void LogSessionTaskAssigningUser(string SessionId, string DeviceSerialNumber, string UserDisplayName, string UserUsername, string UserDomain, string UserSID)
|
||||
{
|
||||
Log(EventTypeIds.SessionTaskAssigningUser, new object[]
|
||||
{
|
||||
SessionId,
|
||||
DeviceSerialNumber,
|
||||
UserDisplayName,
|
||||
UserUsername,
|
||||
UserDomain,
|
||||
UserSID
|
||||
});
|
||||
}
|
||||
public static void LogSessionTaskProvisioningCertificate(string SessionId, string DeviceSerialNumber, string CertificateName)
|
||||
{
|
||||
Log(EventTypeIds.SessionTaskProvisioningCertificate, new object[]
|
||||
{
|
||||
SessionId,
|
||||
DeviceSerialNumber,
|
||||
CertificateName
|
||||
});
|
||||
}
|
||||
public static void LogSessionTaskProvisioningWirelessProfile(string SessionId, string DeviceSerialNumber, string WirelessProfileName)
|
||||
{
|
||||
Log(EventTypeIds.SessionTaskProvisioningWirelessProfile, new object[]
|
||||
{
|
||||
SessionId,
|
||||
DeviceSerialNumber,
|
||||
WirelessProfileName
|
||||
});
|
||||
}
|
||||
public static void LogSessionTaskRenamingDevice(string SessionId, string OldComputerName, string NewComputerName)
|
||||
{
|
||||
Log(EventTypeIds.SessionTaskRenamingDevice, new object[]
|
||||
{
|
||||
SessionId,
|
||||
OldComputerName,
|
||||
NewComputerName
|
||||
});
|
||||
}
|
||||
public static void LogSessionTaskMovingDeviceOrganisationUnit(string SessionId, string OldOrganisationUnit, string NewOrganisationUnit)
|
||||
{
|
||||
Log(EventTypeIds.SessionTaskMovingDeviceOrganisationUnit, new object[]
|
||||
{
|
||||
SessionId,
|
||||
OldOrganisationUnit,
|
||||
NewOrganisationUnit
|
||||
});
|
||||
}
|
||||
public static void LogClientError(string ClientIP, string ClientIdentifier, string ClientVersion, string Error, string RawError)
|
||||
{
|
||||
Log(EventTypeIds.ClientError, new object[]
|
||||
{
|
||||
ClientIP,
|
||||
ClientIdentifier,
|
||||
ClientVersion,
|
||||
Error,
|
||||
RawError
|
||||
});
|
||||
}
|
||||
protected override List<LogEventType> LoadEventTypes()
|
||||
{
|
||||
return new List<LogEventType>
|
||||
{
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionStarting,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Session Starting",
|
||||
Format = "Starting '{2}' Enrolment for {1} (Session# {0})",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionProgress,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Session Progress",
|
||||
Format = "Processing Session# {0}; {1}% Complete; Status: {2}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = false,
|
||||
UseDisplay = false
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionDevice,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Session Device",
|
||||
Format = null,
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = false
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionDeviceInfo,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Session Device Info",
|
||||
Format = null,
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionFinished,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Session Finished",
|
||||
Format = "Finished Session# {0}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionDiagnosticInformation,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Session Diagnostic Information",
|
||||
Format = null,
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = false,
|
||||
UseDisplay = false
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionWarning,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Session Warning",
|
||||
Format = null,
|
||||
Severity = 1,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionError,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Session Error",
|
||||
Format = "An Error Occurred: [{1}] {2}",
|
||||
Severity = 2,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionErrorWithInner,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Session Error with Internal",
|
||||
Format = "An Error Occurred: [{1}] {2}; Internal Error: [{3}] {4}",
|
||||
Severity = 2,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionClientError,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Client Error",
|
||||
Format = "IP: {1}; Device ID: {2}; Version: {3} Error: {4}; Session# {0}",
|
||||
Severity = (int)LogEventType.Severities.Error,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionTaskAddedDevice,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Task - Added Device",
|
||||
Format = "Creating Disco Device {1}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionTaskUpdatingDevice,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Task - Updating Device",
|
||||
Format = "Updating Disco Device {1}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionTaskCreatedDeviceModel,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Task - Creating Device Model",
|
||||
Format = "Creating Device Model '{2} {3}' for Device {1}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionTaskProvisioningADAccount,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Task - Provisioning Active Directory Account",
|
||||
Format = "Provisioning Active Directory Account '{2}' for Device {1}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionTaskAssigningUser,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Task - Assigning User",
|
||||
Format = "Assigning User '{2}' ({4}\\{3} {{{5}}}) for Device {1}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionTaskProvisioningCertificate,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Task - Provisioning Certificate",
|
||||
Format = "Provisioning Certificate '{2}' for Device {1}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionTaskProvisioningWirelessProfile,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Task - Provisioning Wireless Profile",
|
||||
Format = "Provisioning Wireless Profile '{2}' for Device {1}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionTaskRenamingDevice,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Task - Renaming Device",
|
||||
Format = "Renaming Device '{1}' to '{2}'",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.SessionTaskMovingDeviceOrganisationUnit,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Task - Moving Device Organisation Unit",
|
||||
Format = "Moving Device Organisation Unit '{1}' to '{2}'",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ClientError,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Client Error",
|
||||
Format = "IP: {0}; Device ID: {1}; Version: {2} Error: {3}",
|
||||
Severity = (int)LogEventType.Severities.Error,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Disco.Services.Devices.Enrolment
|
||||
{
|
||||
public class EnrolmentSafeException : Exception
|
||||
{
|
||||
public EnrolmentSafeException(string Message) : base(Message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.Services.Devices.Enrolment
|
||||
{
|
||||
public enum EnrolmentTypes
|
||||
{
|
||||
Normal,
|
||||
Mac = 5,
|
||||
MacSecure,
|
||||
Register = 30
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Services.Logging;
|
||||
using Disco.Services.Tasks;
|
||||
using Quartz;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services.Devices.Enrolment
|
||||
{
|
||||
public class LogMacAddressImportingTask : ScheduledTask
|
||||
{
|
||||
public override string TaskName { get { return "Migration: Logs to Device Mac Address Details"; } }
|
||||
|
||||
public override bool SingleInstanceTask { get { return true; } }
|
||||
public override bool CancelInitiallySupported { get { return false; } }
|
||||
|
||||
#region Required Helpers
|
||||
private static string RequiredFilePath(DiscoDataContext Database)
|
||||
{
|
||||
if (Database.DiscoConfiguration.DataStoreLocation != null)
|
||||
return System.IO.Path.Combine(Database.DiscoConfiguration.DataStoreLocation, "_LogMacAddressImportingRequired.txt");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool IsRequired(DiscoDataContext Database)
|
||||
{
|
||||
var requiredFilePath = RequiredFilePath(Database);
|
||||
|
||||
if (requiredFilePath == null)
|
||||
return false;
|
||||
else
|
||||
return System.IO.File.Exists(requiredFilePath);
|
||||
}
|
||||
public static void SetRequired(DiscoDataContext Database)
|
||||
{
|
||||
var requiredFilePath = RequiredFilePath(Database);
|
||||
|
||||
if (requiredFilePath != null)
|
||||
{
|
||||
System.IO.File.WriteAllText(requiredFilePath, "This file exists to indicate an Importing of Mac Address from the Disco Logs is required. It will automatically be deleted when the import completes.");
|
||||
System.IO.File.SetAttributes(requiredFilePath, System.IO.FileAttributes.Hidden);
|
||||
}
|
||||
// ELSE: Could never be required if no DataStoreLocation is set (no logs to process)
|
||||
}
|
||||
#endregion
|
||||
|
||||
public override void InitalizeScheduledTask(DiscoDataContext Database)
|
||||
{
|
||||
if (IsRequired(Database))
|
||||
{
|
||||
// Schedule in 15mins
|
||||
var trigger = TriggerBuilder.Create()
|
||||
.StartAt(DateTimeOffset.Now.AddMinutes(5));
|
||||
|
||||
this.ScheduleTask(trigger);
|
||||
}
|
||||
}
|
||||
|
||||
public static ScheduledTaskStatus ScheduleImmediately()
|
||||
{
|
||||
var existingTask = ScheduledTasks.GetTaskStatuses(typeof(LogMacAddressImportingTask)).Where(s => s.IsRunning).FirstOrDefault();
|
||||
if (existingTask != null)
|
||||
return existingTask;
|
||||
|
||||
var instance = new LogMacAddressImportingTask();
|
||||
return instance.ScheduleTask();
|
||||
}
|
||||
|
||||
protected override void ExecuteTask()
|
||||
{
|
||||
using (DiscoDataContext database = new DiscoDataContext())
|
||||
{
|
||||
Status.UpdateStatus(0, "Importing MAC Addresses", "Querying Logs for Details");
|
||||
// Load Logs
|
||||
var logRetriever = new ReadLogContext()
|
||||
{
|
||||
Module = EnrolmentLog.Current.ModuleId,
|
||||
EventTypes = new List<int>() { (int)EnrolmentLog.EventTypeIds.SessionDeviceInfo }
|
||||
};
|
||||
var results = logRetriever.Query(database);
|
||||
|
||||
Status.UpdateStatus(50, string.Format("Passing {0} logs", results.Count));
|
||||
|
||||
Dictionary<string, Tuple<string, string>> addresses = new Dictionary<string, Tuple<string, string>>();
|
||||
|
||||
foreach (var result in results.OrderBy(r => r.Timestamp))
|
||||
addresses[((string)result.Arguments[1]).ToLower()] = new Tuple<string, string>((string)result.Arguments[4], (string)result.Arguments[5]);
|
||||
|
||||
Status.UpdateStatus(75, string.Format("Importing {0} details", addresses.Count));
|
||||
|
||||
var devices = database.Devices.Include("DeviceDetails").ToList();
|
||||
|
||||
Tuple<string, string> addressResult;
|
||||
foreach (var device in devices)
|
||||
{
|
||||
if (addresses.TryGetValue(device.SerialNumber.ToLower(), out addressResult))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(addressResult.Item1))
|
||||
device.DeviceDetails.LanMacAddress(device, addressResult.Item1);
|
||||
if (!string.IsNullOrEmpty(addressResult.Item2))
|
||||
device.DeviceDetails.WLanMacAddress(device, addressResult.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
Status.UpdateStatus(90, "Saving to Database");
|
||||
|
||||
database.SaveChanges();
|
||||
|
||||
// Finished - Remove Placeholder File
|
||||
var requiredFilePath = RequiredFilePath(database);
|
||||
if (System.IO.File.Exists(requiredFilePath))
|
||||
System.IO.File.Delete(requiredFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ namespace Disco.Services.Devices.Exporting
|
||||
}
|
||||
public static DeviceExportResult GenerateExport(DiscoDataContext Database, IQueryable<Device> Devices, DeviceExportOptions Options)
|
||||
{
|
||||
return GenerateExport(Database, Devices, Options, ScheduledTaskMockStatus.Create());
|
||||
return GenerateExport(Database, Devices, Options, ScheduledTaskMockStatus.Create("Device Export"));
|
||||
}
|
||||
|
||||
public static DeviceExportResult GenerateExport(DiscoDataContext Database, DeviceExportOptions Options, IScheduledTaskStatus TaskStatus)
|
||||
@@ -134,7 +134,7 @@ namespace Disco.Services.Devices.Exporting
|
||||
}
|
||||
public static DeviceExportResult GenerateExport(DiscoDataContext Database, DeviceExportOptions Options)
|
||||
{
|
||||
return GenerateExport(Database, Options, ScheduledTaskMockStatus.Create());
|
||||
return GenerateExport(Database, Options, ScheduledTaskMockStatus.Create("Device Export"));
|
||||
}
|
||||
|
||||
private static IEnumerable<DeviceExportRecord> BuildRecords(IQueryable<Device> Devices)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<TargetFrameworkProfile />
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
<AssemblyVersion>2.2.16272.1003</AssemblyVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -82,6 +83,10 @@
|
||||
<HintPath>..\packages\PDFsharp.1.50.4000-beta3b\lib\net20\PdfSharp.Charting.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="PList, Version=0.1.4109.38751, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\plist.net.1.0\lib\Net35\PList.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Quartz">
|
||||
<HintPath>..\Resources\Libraries\Quartz\Quartz.dll</HintPath>
|
||||
</Reference>
|
||||
@@ -89,6 +94,9 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\RazorGenerator.Mvc.2.2.3\lib\net40\RazorGenerator.Mvc.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Renci.SshNet">
|
||||
<HintPath>..\..\..\Resources\Libraries\SshNet\Renci.SshNet.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Spring.Core">
|
||||
<HintPath>..\..\..\Resources\Libraries\Spring.NET\Spring.Core.dll</HintPath>
|
||||
</Reference>
|
||||
@@ -177,6 +185,7 @@
|
||||
<Compile Include="Attachments\AttachmentDataStoreExtensions.cs" />
|
||||
<Compile Include="Authorization\AccessDeniedException.cs" />
|
||||
<Compile Include="Authorization\AuthorizationLog.cs" />
|
||||
<Compile Include="Authorization\AuthorizationRoleExtensions.cs" />
|
||||
<Compile Include="Authorization\AuthorizationToken.cs" />
|
||||
<Compile Include="Authorization\ClaimNavigatorItem.cs" />
|
||||
<Compile Include="Authorization\DiscoAuthorizeBaseAttribute.cs" />
|
||||
@@ -220,8 +229,23 @@
|
||||
<Compile Include="Authorization\Roles\RoleCache.cs" />
|
||||
<Compile Include="Authorization\Roles\RoleClaims.cs" />
|
||||
<Compile Include="Authorization\Roles\RoleToken.cs" />
|
||||
<Compile Include="ClientServices\ClientServicesExtensions.cs" />
|
||||
<Compile Include="DataStore.cs" />
|
||||
<Compile Include="Devices\DeviceActionExtensions.cs" />
|
||||
<Compile Include="Devices\DeviceBatches.cs" />
|
||||
<Compile Include="Devices\DeviceBatchExtensions.cs" />
|
||||
<Compile Include="Devices\DeviceCertificateExtensions.cs" />
|
||||
<Compile Include="Devices\DeviceDataStoreExtensions.cs" />
|
||||
<Compile Include="Devices\DeviceDetailExtensions.cs" />
|
||||
<Compile Include="Devices\DeviceExtensions.cs" />
|
||||
<Compile Include="Devices\DeviceModelExtensions.cs" />
|
||||
<Compile Include="Devices\DeviceProfileExtensions.cs" />
|
||||
<Compile Include="Devices\DeviceWirelessProfileExtensions.cs" />
|
||||
<Compile Include="Devices\Enrolment\DeviceEnrolment.cs" />
|
||||
<Compile Include="Devices\Enrolment\EnrolmentLog.cs" />
|
||||
<Compile Include="Devices\Enrolment\EnrolmentSafeException.cs" />
|
||||
<Compile Include="Devices\Enrolment\EnrolmentTypes.cs" />
|
||||
<Compile Include="Devices\Enrolment\LogMacAddressImportingTask.cs" />
|
||||
<Compile Include="Devices\Exporting\DeviceExport.cs" />
|
||||
<Compile Include="Devices\Exporting\DeviceExportTask.cs" />
|
||||
<Compile Include="Devices\Exporting\DeviceExportTaskContext.cs" />
|
||||
@@ -260,12 +284,16 @@
|
||||
<Compile Include="Documents\AttachmentImport\ImporterJob.cs" />
|
||||
<Compile Include="Documents\AttachmentImport\ImportDirectoryMonitor.cs" />
|
||||
<Compile Include="Documents\AttachmentImport\ImportPage.cs" />
|
||||
<Compile Include="Documents\AttachmentImport\ThumbnailUpdateTask.cs" />
|
||||
<Compile Include="Documents\DocumentsLog.cs" />
|
||||
<Compile Include="Documents\DocumentTemplateExtensions.cs" />
|
||||
<Compile Include="Documents\DocumentTemplateDataStoreExtensions.cs" />
|
||||
<Compile Include="Documents\DocumentTemplateExpressionExtensions.cs" />
|
||||
<Compile Include="Documents\DocumentUniqueIdentifier.cs" />
|
||||
<Compile Include="Documents\DocumentUniqueIdentifierExtensions.cs" />
|
||||
<Compile Include="Documents\ManagedGroups\DocumentTemplateDevicesManagedGroup.cs" />
|
||||
<Compile Include="Documents\ManagedGroups\DocumentTemplateManagedGroups.cs" />
|
||||
<Compile Include="Documents\ManagedGroups\DocumentTemplateUsersManagedGroup.cs" />
|
||||
<Compile Include="Documents\QRCodeBinaryEncoder.cs" />
|
||||
<Compile Include="Expressions\EvaluateExpressionParseException.cs" />
|
||||
<Compile Include="Expressions\EvaluateExpressionPart.cs" />
|
||||
@@ -319,18 +347,23 @@
|
||||
<Compile Include="Interop\VicEduDept\VicSmart.cs" />
|
||||
<Compile Include="Interop\DiscoServices\UpdateQuery.cs" />
|
||||
<Compile Include="Interop\DiscoServices\UpdateQueryTask.cs" />
|
||||
<Compile Include="Jobs\JobActionExtensions.cs" />
|
||||
<Compile Include="Jobs\JobExtensions.cs" />
|
||||
<Compile Include="Jobs\JobFlagExtensions.cs" />
|
||||
<Compile Include="Jobs\JobLists\JobTableExtensions.cs" />
|
||||
<Compile Include="Jobs\JobQueues\Cache.cs" />
|
||||
<Compile Include="Jobs\JobQueues\JobQueueDeleteTask.cs" />
|
||||
<Compile Include="Jobs\JobQueues\JobQueueExtensions.cs" />
|
||||
<Compile Include="Jobs\JobQueues\JobQueueService.cs" />
|
||||
<Compile Include="Jobs\JobQueues\JobQueueToken.cs" />
|
||||
<Compile Include="Jobs\JobLists\ManagedJobList.cs" />
|
||||
<Compile Include="Jobs\Jobs.cs" />
|
||||
<Compile Include="Jobs\JobUpdatesHub.cs" />
|
||||
<Compile Include="Jobs\Noticeboards\HeldDeviceItem.cs" />
|
||||
<Compile Include="Jobs\Noticeboards\NoticeboardUpdatesHub.cs" />
|
||||
<Compile Include="Jobs\Noticeboards\HeldDevices.cs" />
|
||||
<Compile Include="Jobs\Noticeboards\HeldDevicesForUsers.cs" />
|
||||
<Compile Include="Jobs\Statistics\DailyOpenedClosed.cs" />
|
||||
<Compile Include="Logging\LogBase.cs" />
|
||||
<Compile Include="Logging\LogContext.cs" />
|
||||
<Compile Include="Logging\LogReInitalizeJob.cs" />
|
||||
@@ -343,6 +376,12 @@
|
||||
<Compile Include="Logging\Persistance\LogPersistContext.cs" />
|
||||
<Compile Include="Logging\Persistance\LogPersistContextInitializer.cs" />
|
||||
<Compile Include="Logging\Utilities.cs" />
|
||||
<Compile Include="Plugins\Features\CertificateAuthorityProvider\CertificateAuthorityProviderFeature.cs" />
|
||||
<Compile Include="Plugins\Features\CertificateProvider\ProvisionPersonalCertificateResult.cs" />
|
||||
<Compile Include="Plugins\Features\CertificateAuthorityProvider\ProvisionAuthorityCertificatesResult.cs" />
|
||||
<Compile Include="Plugins\Features\WirelessProfileProvider\ProvisionWirelessProfilesResult.cs" />
|
||||
<Compile Include="Plugins\Features\WirelessProfileProvider\WirelessProfile.cs" />
|
||||
<Compile Include="Plugins\Features\WirelessProfileProvider\WirelessProfileTransformation.cs" />
|
||||
<Compile Include="Plugins\Features\RepairProvider\RepairProviderFeature.cs" />
|
||||
<Compile Include="Plugins\Features\RepairProvider\RepairProviderSubmitJobException.cs" />
|
||||
<Compile Include="Plugins\Features\UIExtension\Results\LiteralResult.cs" />
|
||||
@@ -352,6 +391,7 @@
|
||||
<Compile Include="Plugins\Features\UIExtension\Results\PrecompiledPartialViewResult.cs" />
|
||||
<Compile Include="Plugins\Features\UIExtension\UIExtensionResult.cs" />
|
||||
<Compile Include="Plugins\Features\UIExtension\UIExtensionFeature.cs" />
|
||||
<Compile Include="Plugins\Features\WirelessProfileProvider\WirelessProfileProviderFeature.cs" />
|
||||
<Compile Include="Plugins\UpdatePluginsAfterDiscoUpdateTask.cs" />
|
||||
<Compile Include="Plugins\UpdatePluginTask.cs" />
|
||||
<Compile Include="Plugins\InstallPluginTask.cs" />
|
||||
@@ -400,6 +440,7 @@
|
||||
<Compile Include="Users\CacheCleanTask.cs" />
|
||||
<Compile Include="Users\UserExtensions.cs" />
|
||||
<Compile Include="Users\UserFlags\Cache.cs" />
|
||||
<Compile Include="Users\UserFlags\UserFlagExtensions.cs" />
|
||||
<Compile Include="Users\UserFlags\UserFlagUserDevicesManagedGroup.cs" />
|
||||
<Compile Include="Users\UserFlags\UserFlagUsersManagedGroup.cs" />
|
||||
<Compile Include="Users\UserFlags\UserFlagsBulkAssignTask.cs" />
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Tasks;
|
||||
using Quartz;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Disco.Services.Documents.AttachmentImport
|
||||
{
|
||||
public class ThumbnailUpdateTask : ScheduledTask
|
||||
{
|
||||
public override string TaskName { get { return "Migration: PDF Attachment Thumbnail Update"; } }
|
||||
|
||||
public override bool SingleInstanceTask { get { return true; } }
|
||||
public override bool CancelInitiallySupported { get { return false; } }
|
||||
|
||||
#region Required Helpers
|
||||
private static string RequiredFilePath(DiscoDataContext Database)
|
||||
{
|
||||
if (Database.DiscoConfiguration.DataStoreLocation != null)
|
||||
return Path.Combine(Database.DiscoConfiguration.DataStoreLocation, "_ThumbnailUpdateRequired.txt");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool IsRequired(DiscoDataContext Database)
|
||||
{
|
||||
var requiredFilePath = RequiredFilePath(Database);
|
||||
|
||||
if (requiredFilePath == null)
|
||||
return false;
|
||||
else
|
||||
return File.Exists(requiredFilePath);
|
||||
}
|
||||
public static void SetRequired(DiscoDataContext Database)
|
||||
{
|
||||
var requiredFilePath = RequiredFilePath(Database);
|
||||
|
||||
if (requiredFilePath != null)
|
||||
{
|
||||
File.WriteAllText(requiredFilePath, "This file exists to indicate a Thumbnail Update is required. It will automatically be deleted when the update completes.");
|
||||
File.SetAttributes(requiredFilePath, FileAttributes.Hidden);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public override void InitalizeScheduledTask(DiscoDataContext Database)
|
||||
{
|
||||
if (IsRequired(Database))
|
||||
{
|
||||
// Schedule in 5mins
|
||||
var trigger = TriggerBuilder.Create()
|
||||
.StartAt(DateTimeOffset.Now.AddMinutes(5));
|
||||
|
||||
this.ScheduleTask(trigger);
|
||||
}
|
||||
}
|
||||
|
||||
public static ScheduledTaskStatus ScheduleImmediately()
|
||||
{
|
||||
var existingTask = ScheduledTasks.GetTaskStatuses(typeof(ThumbnailUpdateTask)).Where(s => s.IsRunning).FirstOrDefault();
|
||||
if (existingTask != null)
|
||||
return existingTask;
|
||||
|
||||
var instance = new ThumbnailUpdateTask();
|
||||
return instance.ScheduleTask();
|
||||
}
|
||||
|
||||
protected override void ExecuteTask()
|
||||
{
|
||||
using (DiscoDataContext database = new DiscoDataContext())
|
||||
{
|
||||
Status.UpdateStatus(0, "Updating Attachment PDF Thumbnails", "Reading Attachments");
|
||||
|
||||
// Attachments
|
||||
List<IAttachment> attachments = database
|
||||
.DeviceAttachments.Where(da => da.MimeType == "application/pdf").ToList().Cast<IAttachment>()
|
||||
.Concat(database.UserAttachments.Where(da => da.MimeType == "application/pdf").ToList().Cast<IAttachment>())
|
||||
.Concat(database.JobAttachments.Where(da => da.MimeType == "application/pdf").ToList().Cast<IAttachment>())
|
||||
.ToList();
|
||||
|
||||
int failedTotal = 0;
|
||||
int notFoundTotal = 0;
|
||||
int completedTotal = 0;
|
||||
|
||||
attachments.AsParallel().ForAll(a =>
|
||||
{
|
||||
var completed = Interlocked.Increment(ref completedTotal);
|
||||
if ((completed % 10) == 0)
|
||||
{
|
||||
Status.UpdateStatus((100D / attachments.Count) * completed, $"Processing: {a.AttachmentType} Attachment, Id: {a.Id}");
|
||||
}
|
||||
try
|
||||
{
|
||||
var fileName = a.RepositoryFilename(database);
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
Interlocked.Increment(ref notFoundTotal);
|
||||
Status.LogWarning($"Attachment not found in the Data Store. [{a.AttachmentType} Attachment, Id {a.Id}] expected at: '{fileName}'");
|
||||
}
|
||||
else
|
||||
{
|
||||
a.GenerateThumbnail(database);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Interlocked.Increment(ref failedTotal);
|
||||
Status.LogWarning($"Error when generating thumbnail for [{a.AttachmentType} Attachment, Id {a.Id}]: [{ex.GetType().Name}] {ex.Message}");
|
||||
}
|
||||
});
|
||||
|
||||
// Finished - Remove Placeholder File
|
||||
var requiredFilePath = RequiredFilePath(database);
|
||||
if (requiredFilePath != null && File.Exists(requiredFilePath))
|
||||
File.Delete(requiredFilePath);
|
||||
|
||||
Status.SetFinishedMessage($"Finished updating thumbnails for {attachments.Count:N0}. {notFoundTotal:N0} were not found. {failedTotal:N0} failed.");
|
||||
Status.LogInformation(Status.FinishedMessage);
|
||||
Status.Finished();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Data.Repository.Monitor;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Interop.ActiveDirectory;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace Disco.Services.Documents.ManagedGroups
|
||||
{
|
||||
public class DocumentTemplateDevicesManagedGroup : ADManagedGroup
|
||||
{
|
||||
private const string KeyFormat = "DocumentTemplate_{0}_Devices";
|
||||
private const string DeviceDescriptionFormat = "Devices with a {0} attachment will be added to this Active Directory group.";
|
||||
private const string DescriptionFormat = "{0}s with a {1} attachment will have any associated devices added to this Active Directory group.";
|
||||
private const string CategoryDescriptionFormat = "Related Devices Linked Group";
|
||||
private const string GroupDescriptionFormat = "{0} [Document Template Devices]";
|
||||
|
||||
private IDisposable repositoryAddSubscription;
|
||||
private IDisposable repositoryRemoveSubscription;
|
||||
private IDisposable deviceRenameRepositorySubscription;
|
||||
private IDisposable jobCloseRepositorySubscription;
|
||||
private IDisposable deviceAssignmentRepositorySubscription;
|
||||
private string DocumentTemplateId;
|
||||
private string DocumentTemplateDescription;
|
||||
private string DocumentTemplateScope;
|
||||
|
||||
public override string Description { get { return GetDescription(DocumentTemplateScope, DocumentTemplateDescription); } }
|
||||
public override string CategoryDescription { get { return CategoryDescriptionFormat; } }
|
||||
public override string GroupDescription { get { return string.Format(GroupDescriptionFormat, DocumentTemplateDescription); } }
|
||||
public override bool IncludeFilterBeginDate { get { return true; } }
|
||||
|
||||
private DocumentTemplateDevicesManagedGroup(string Key, ADManagedGroupConfiguration Configuration, DocumentTemplate DocumentTemplate)
|
||||
: base(Key, Configuration)
|
||||
{
|
||||
this.DocumentTemplateId = DocumentTemplate.Id;
|
||||
this.DocumentTemplateDescription = DocumentTemplate.Description;
|
||||
this.DocumentTemplateScope = DocumentTemplate.Scope;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
// Subscribe to changes
|
||||
switch (DocumentTemplateScope)
|
||||
{
|
||||
case DocumentTemplate.DocumentTemplateScopes.Device:
|
||||
// Observe Device Attachments
|
||||
repositoryAddSubscription = DocumentTemplateManagedGroups.DeviceAttachmentAddRepositoryEvents.Value
|
||||
.Where(e => ((DeviceAttachment)e.Entity).DocumentTemplateId == DocumentTemplateId)
|
||||
.Subscribe(ProcessDeviceAttachmentAddEvent);
|
||||
repositoryRemoveSubscription = DocumentTemplateManagedGroups.DeviceAttachmentRemoveEvents.Value
|
||||
.Where(e => e.Item3 == DocumentTemplateId)
|
||||
.Subscribe(ProcessDeviceAttachmentRemoveEvent);
|
||||
break;
|
||||
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||
// Observe Job Attachments
|
||||
repositoryAddSubscription = DocumentTemplateManagedGroups.JobAttachmentAddRepositoryEvents.Value
|
||||
.Where(e => ((JobAttachment)e.Entity).DocumentTemplateId == DocumentTemplateId)
|
||||
.Subscribe(ProcessJobAttachmentAddEvent);
|
||||
repositoryRemoveSubscription = DocumentTemplateManagedGroups.JobAttachmentRemoveEvents.Value
|
||||
.Where(e => e.Item3 == DocumentTemplateId)
|
||||
.Subscribe(ProcessJobAttachmentRemoveEvent);
|
||||
// Observe Job Close/Reopen
|
||||
jobCloseRepositorySubscription = DocumentTemplateManagedGroups.JobCloseRepositoryEvents.Value
|
||||
.Subscribe(ProcessJobCloseRepositoryEvent);
|
||||
break;
|
||||
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||
// Observe User Attachments
|
||||
repositoryAddSubscription = DocumentTemplateManagedGroups.UserAttachmentAddRepositoryEvents.Value
|
||||
.Where(e => ((UserAttachment)e.Entity).DocumentTemplateId == DocumentTemplateId)
|
||||
.Subscribe(ProcessUserAttachmentAddEvent);
|
||||
repositoryRemoveSubscription = DocumentTemplateManagedGroups.UserAttachmentRemoveEvents.Value
|
||||
.Where(e => e.Item3 == DocumentTemplateId)
|
||||
.Subscribe(ProcessUserAttachmentRemoveEvent);
|
||||
// Observe Device Assignments
|
||||
deviceAssignmentRepositorySubscription = DocumentTemplateManagedGroups.DeviceAssignmentRepositoryEvents.Value
|
||||
.Subscribe(ProcessDeviceAssignmentRepositoryEvent);
|
||||
break;
|
||||
}
|
||||
|
||||
// Observe Device Renaming (DeviceDomainId)
|
||||
deviceRenameRepositorySubscription = DocumentTemplateManagedGroups.DeviceRenameRepositoryEvents.Value
|
||||
.Subscribe(ProcessDeviceRenameRepositoryEvent);
|
||||
}
|
||||
|
||||
public static string GetKey(DocumentTemplate DocumentTemplate)
|
||||
{
|
||||
return string.Format(KeyFormat, DocumentTemplate.Id);
|
||||
}
|
||||
private static string GetDescription(string DocumentTemplateScope, string DocumentTemplateDescription)
|
||||
{
|
||||
switch (DocumentTemplateScope)
|
||||
{
|
||||
case DocumentTemplate.DocumentTemplateScopes.Device:
|
||||
return string.Format(DeviceDescriptionFormat, DocumentTemplateDescription);
|
||||
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||
return string.Format(DescriptionFormat, DocumentTemplateScope, DocumentTemplateDescription);
|
||||
default:
|
||||
throw new ArgumentException("Unknown Document Template Scope", "Scope");
|
||||
}
|
||||
}
|
||||
public static string GetDescription(DocumentTemplate DocumentTemplate)
|
||||
{
|
||||
return GetDescription(DocumentTemplate.Scope, DocumentTemplate.Description);
|
||||
}
|
||||
public static string GetCategoryDescription(DocumentTemplate DocumentTemplate)
|
||||
{
|
||||
return CategoryDescriptionFormat;
|
||||
}
|
||||
|
||||
public static bool TryGetManagedGroup(DocumentTemplate DocumentTemplate, out DocumentTemplateDevicesManagedGroup ManagedGroup)
|
||||
{
|
||||
ADManagedGroup managedGroup;
|
||||
string key = GetKey(DocumentTemplate);
|
||||
|
||||
if (ActiveDirectory.Context.ManagedGroups.TryGetValue(key, out managedGroup))
|
||||
{
|
||||
ManagedGroup = (DocumentTemplateDevicesManagedGroup)managedGroup;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ManagedGroup = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static DocumentTemplateDevicesManagedGroup Initialize(DocumentTemplate Template)
|
||||
{
|
||||
var key = GetKey(Template);
|
||||
|
||||
if (!string.IsNullOrEmpty(Template.DevicesLinkedGroup))
|
||||
{
|
||||
var config = ADManagedGroup.ConfigurationFromJson(Template.DevicesLinkedGroup);
|
||||
|
||||
if (config != null && !string.IsNullOrWhiteSpace(config.GroupId))
|
||||
{
|
||||
var group = new DocumentTemplateDevicesManagedGroup(
|
||||
key,
|
||||
config,
|
||||
Template);
|
||||
|
||||
// Add to AD Context
|
||||
ActiveDirectory.Context.ManagedGroups.AddOrUpdate(group);
|
||||
|
||||
return group;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from AD Context
|
||||
ActiveDirectory.Context.ManagedGroups.Remove(key);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> DetermineMembers(DiscoDataContext Database)
|
||||
{
|
||||
switch (DocumentTemplateScope)
|
||||
{
|
||||
case DocumentTemplate.DocumentTemplateScopes.Device:
|
||||
return Database.Devices
|
||||
.Where(d => d.DeviceDomainId != null && d.DeviceAttachments.Any(a => a.DocumentTemplateId == this.DocumentTemplateId))
|
||||
.Select(d => d.DeviceDomainId)
|
||||
.ToList()
|
||||
.Where(ActiveDirectory.IsValidDomainAccountId)
|
||||
.Select(id => id + "$");
|
||||
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||
return Database.Jobs
|
||||
.Where(j => !j.ClosedDate.HasValue && j.Device.DeviceDomainId != null && j.JobAttachments.Any(a => a.DocumentTemplateId == this.DocumentTemplateId))
|
||||
.Select(j => j.Device.DeviceDomainId)
|
||||
.Distinct()
|
||||
.ToList()
|
||||
.Where(ActiveDirectory.IsValidDomainAccountId)
|
||||
.Select(id => id + "$");
|
||||
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||
return Database.Users
|
||||
.Where(u => u.UserAttachments.Any(a => a.DocumentTemplateId == this.DocumentTemplateId))
|
||||
.SelectMany(u => u.DeviceUserAssignments.Where(dua => !dua.UnassignedDate.HasValue && dua.Device.DeviceDomainId != null), (u, dua) => dua.Device.DeviceDomainId)
|
||||
.ToList()
|
||||
.Where(ActiveDirectory.IsValidDomainAccountId)
|
||||
.Select(id => id + "$");
|
||||
default:
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
}
|
||||
|
||||
#region Device Scope
|
||||
private bool DeviceContainsAttachment(DiscoDataContext Database, string DeviceSerialNumber, out string DeviceAccountId)
|
||||
{
|
||||
var result = Database.Devices
|
||||
.Where(d => d.SerialNumber == DeviceSerialNumber && d.DeviceDomainId != null)
|
||||
.Select(d => new Tuple<string, bool>(d.DeviceDomainId, d.DeviceAttachments.Any(a => a.DocumentTemplateId == this.DocumentTemplateId)))
|
||||
.FirstOrDefault();
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
DeviceAccountId = null;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ActiveDirectory.IsValidDomainAccountId(result.Item1))
|
||||
{
|
||||
DeviceAccountId = result.Item1 + "$";
|
||||
return result.Item2;
|
||||
}
|
||||
else
|
||||
{
|
||||
DeviceAccountId = result.Item1 + "$";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessDeviceAttachmentAddEvent(RepositoryMonitorEvent e)
|
||||
{
|
||||
var attachment = (DeviceAttachment)e.Entity;
|
||||
|
||||
string deviceAccountId;
|
||||
if (DeviceContainsAttachment(e.Database, attachment.DeviceSerialNumber, out deviceAccountId))
|
||||
AddMember(attachment.DeviceSerialNumber, (database) => new string[] { deviceAccountId });
|
||||
}
|
||||
private void ProcessDeviceAttachmentRemoveEvent(Tuple<DiscoDataContext, int, string, string> e)
|
||||
{
|
||||
var deviceSerialNumber = e.Item3;
|
||||
|
||||
RemoveMember(deviceSerialNumber, (database) =>
|
||||
{
|
||||
string deviceAccountId;
|
||||
if (!DeviceContainsAttachment(database, deviceSerialNumber, out deviceAccountId) && deviceAccountId != null)
|
||||
return new string[] { deviceAccountId };
|
||||
else
|
||||
return null;
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Job Scope
|
||||
private bool JobsContainAttachment(DiscoDataContext Database, int JobId, out string DeviceAccountId, out string DeviceSerialNumber)
|
||||
{
|
||||
var result = Database.Jobs
|
||||
.Where(j => j.Id == JobId && j.Device.DeviceDomainId != null)
|
||||
.Select(j => new Tuple<string, string, bool>(
|
||||
j.Device.DeviceDomainId,
|
||||
j.Device.SerialNumber,
|
||||
j.Device.Jobs.Where(dj => !dj.ClosedDate.HasValue).Any(dj => dj.JobAttachments.Any(a => a.DocumentTemplateId == this.DocumentTemplateId)))
|
||||
).FirstOrDefault();
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
DeviceAccountId = null;
|
||||
DeviceSerialNumber = null;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ActiveDirectory.IsValidDomainAccountId(result.Item1))
|
||||
{
|
||||
DeviceAccountId = result.Item1 + "$";
|
||||
DeviceSerialNumber = result.Item2;
|
||||
return result.Item3;
|
||||
}
|
||||
else
|
||||
{
|
||||
DeviceAccountId = result.Item1 + "$";
|
||||
DeviceSerialNumber = result.Item2;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessJobAttachmentAddEvent(RepositoryMonitorEvent e)
|
||||
{
|
||||
var attachment = (JobAttachment)e.Entity;
|
||||
|
||||
string deviceAccountId;
|
||||
string deviceSerialNumber;
|
||||
if (JobsContainAttachment(e.Database, attachment.JobId, out deviceAccountId, out deviceSerialNumber))
|
||||
AddMember(deviceSerialNumber, (database) => new string[] { deviceAccountId });
|
||||
}
|
||||
private void ProcessJobAttachmentRemoveEvent(Tuple<DiscoDataContext, int, string, int> e)
|
||||
{
|
||||
var jobId = e.Item4;
|
||||
string deviceSerialNumber = e.Item1.Jobs.Where(j => j.Id == jobId && j.DeviceSerialNumber != null).Select(j => j.DeviceSerialNumber).FirstOrDefault();
|
||||
|
||||
if (deviceSerialNumber != null)
|
||||
{
|
||||
RemoveMember(deviceSerialNumber, (database) =>
|
||||
{
|
||||
string deviceAccountId;
|
||||
if (!JobsContainAttachment(database, jobId, out deviceAccountId, out deviceSerialNumber) &&
|
||||
deviceSerialNumber != null && deviceAccountId != null)
|
||||
return new string[] { deviceAccountId };
|
||||
else
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region User Scope
|
||||
private bool DeviceUserContainAttachment(DiscoDataContext Database, string UserId, out List<Tuple<string, string>> Devices)
|
||||
{
|
||||
var result = Database.Users
|
||||
.Where(u => u.UserId == UserId)
|
||||
.Select(u => new Tuple<bool, IEnumerable<Tuple<string, string>>>(
|
||||
u.UserAttachments.Any(a => a.DocumentTemplateId == this.DocumentTemplateId),
|
||||
u.DeviceUserAssignments
|
||||
.Where(dua => !dua.UnassignedDate.HasValue && dua.Device.DeviceDomainId != null)
|
||||
.Select(dua => new Tuple<string, string>(dua.Device.DeviceDomainId, dua.Device.SerialNumber)))
|
||||
).FirstOrDefault();
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
Devices = null;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Devices = result.Item2
|
||||
.Where(d => ActiveDirectory.IsValidDomainAccountId(d.Item1))
|
||||
.Select(d => Tuple.Create(d.Item1 + "$", d.Item2))
|
||||
.ToList();
|
||||
return result.Item1;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessUserAttachmentAddEvent(RepositoryMonitorEvent e)
|
||||
{
|
||||
var attachment = (UserAttachment)e.Entity;
|
||||
|
||||
List<Tuple<string, string>> devices;
|
||||
if (DeviceUserContainAttachment(e.Database, attachment.UserId, out devices) && devices != null)
|
||||
devices.ForEach(d => AddMember(d.Item2, (database) => new string[] { d.Item1 }));
|
||||
}
|
||||
private void ProcessUserAttachmentRemoveEvent(Tuple<DiscoDataContext, int, string, string> e)
|
||||
{
|
||||
var userId = e.Item4;
|
||||
|
||||
RemoveMember(userId, (database) =>
|
||||
{
|
||||
List<Tuple<string, string>> devices;
|
||||
if (!DeviceUserContainAttachment(database, userId, out devices) && devices != null)
|
||||
return devices.Select(d => d.Item1);
|
||||
else
|
||||
return null;
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void ProcessDeviceRenameRepositoryEvent(RepositoryMonitorEvent Event)
|
||||
{
|
||||
var device = (Device)Event.Entity;
|
||||
var deviceSerialNumber = device.SerialNumber;
|
||||
var deviceAccountId = device.DeviceDomainId;
|
||||
var deviceAccountIdValid = ActiveDirectory.IsValidDomainAccountId(deviceAccountId);
|
||||
var devicePreviousAccountId = Event.GetPreviousPropertyValue<string>("DeviceDomainId");
|
||||
var devicePreviousAccountIdValid = ActiveDirectory.IsValidDomainAccountId(devicePreviousAccountId);
|
||||
|
||||
if (deviceAccountIdValid || devicePreviousAccountIdValid)
|
||||
{
|
||||
Event.ExecuteAfterCommit(e =>
|
||||
{
|
||||
switch (DocumentTemplateScope)
|
||||
{
|
||||
case DocumentTemplate.DocumentTemplateScopes.Device:
|
||||
if (DeviceContainsAttachment(e.Database, device.SerialNumber, out deviceAccountId))
|
||||
{
|
||||
if (deviceAccountIdValid)
|
||||
AddMember(device.SerialNumber, (database) => new string[] { deviceAccountId });
|
||||
if (devicePreviousAccountIdValid)
|
||||
RemoveMember(device.SerialNumber, (database) => new string[] { devicePreviousAccountId + "$" });
|
||||
}
|
||||
break;
|
||||
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||
var jobsHaveTemplate = e.Database.Jobs
|
||||
.Where(j => !j.ClosedDate.HasValue && j.DeviceSerialNumber == deviceSerialNumber)
|
||||
.Any(j => j.JobAttachments.Any(a => a.DocumentTemplateId == this.DocumentTemplateId));
|
||||
|
||||
if (jobsHaveTemplate)
|
||||
{
|
||||
if (deviceAccountIdValid)
|
||||
AddMember(device.SerialNumber, (database) => new string[] { deviceAccountId + "$" });
|
||||
if (devicePreviousAccountIdValid)
|
||||
RemoveMember(device.SerialNumber, (database) => new string[] { devicePreviousAccountId + "$" });
|
||||
}
|
||||
break;
|
||||
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||
var userHasTemplate = e.Database.Devices
|
||||
.Where(d => d.SerialNumber == deviceSerialNumber)
|
||||
.Select(d => d.AssignedUser)
|
||||
.Any(u => u.UserAttachments.Any(a => a.DocumentTemplateId == this.DocumentTemplateId));
|
||||
|
||||
if (userHasTemplate)
|
||||
{
|
||||
if (deviceAccountIdValid)
|
||||
AddMember(device.SerialNumber, (database) => new string[] { deviceAccountId + "$" });
|
||||
if (devicePreviousAccountIdValid)
|
||||
RemoveMember(device.SerialNumber, (database) => new string[] { devicePreviousAccountId + "$" });
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessJobCloseRepositoryEvent(RepositoryMonitorEvent e)
|
||||
{
|
||||
var job = (Job)e.Entity;
|
||||
|
||||
if (job.DeviceSerialNumber != null)
|
||||
{
|
||||
var jobId = job.Id;
|
||||
|
||||
var relevantJob = e.Database.Jobs
|
||||
.Where(j => j.Id == jobId && j.JobAttachments.Any(ja => ja.DocumentTemplateId == this.DocumentTemplateId))
|
||||
.Any();
|
||||
|
||||
if (relevantJob)
|
||||
{
|
||||
string deviceAccountId;
|
||||
string deviceSerialNumber;
|
||||
if (JobsContainAttachment(e.Database, jobId, out deviceAccountId, out deviceSerialNumber))
|
||||
AddMember(deviceSerialNumber, (database) => new string[] { deviceAccountId });
|
||||
else
|
||||
RemoveMember(deviceSerialNumber, (database) => new string[] { deviceAccountId });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessDeviceAssignmentRepositoryEvent(RepositoryMonitorEvent Event)
|
||||
{
|
||||
var device = (Device)Event.Entity;
|
||||
var deviceSerialNumber = device.SerialNumber;
|
||||
var deviceAccountId = device.DeviceDomainId;
|
||||
|
||||
if (ActiveDirectory.IsValidDomainAccountId(deviceAccountId))
|
||||
{
|
||||
var deviceCurrentAssignedUserId = device.AssignedUserId;
|
||||
var devicePreviousAssignedUserId = Event.GetPreviousPropertyValue<string>("AssignedUserId");
|
||||
|
||||
Event.ExecuteAfterCommit(e =>
|
||||
{
|
||||
bool previousUserHasTemplate = false;
|
||||
bool currentUserHasTemplate = false;
|
||||
|
||||
if (devicePreviousAssignedUserId != null)
|
||||
previousUserHasTemplate = e.Database.Users
|
||||
.Where(u => u.UserId == devicePreviousAssignedUserId && u.UserAttachments.Any(ua => ua.DocumentTemplateId == this.DocumentTemplateId))
|
||||
.Any();
|
||||
|
||||
if (deviceCurrentAssignedUserId != null)
|
||||
currentUserHasTemplate = e.Database.Users
|
||||
.Where(u => u.UserId == deviceCurrentAssignedUserId && u.UserAttachments.Any(ua => ua.DocumentTemplateId == this.DocumentTemplateId))
|
||||
.Any();
|
||||
|
||||
if (!previousUserHasTemplate && currentUserHasTemplate)
|
||||
AddMember(deviceSerialNumber, (database) => new string[] { deviceAccountId + "$" });
|
||||
else if (previousUserHasTemplate && !currentUserHasTemplate)
|
||||
RemoveMember(deviceSerialNumber, (database) => new string[] { deviceAccountId + "$" });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (repositoryAddSubscription != null)
|
||||
repositoryAddSubscription.Dispose();
|
||||
|
||||
if (repositoryRemoveSubscription != null)
|
||||
repositoryRemoveSubscription.Dispose();
|
||||
|
||||
if (deviceRenameRepositorySubscription != null)
|
||||
deviceRenameRepositorySubscription.Dispose();
|
||||
|
||||
if (jobCloseRepositorySubscription != null)
|
||||
jobCloseRepositorySubscription.Dispose();
|
||||
|
||||
if (deviceAssignmentRepositorySubscription != null)
|
||||
deviceAssignmentRepositorySubscription.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Data.Repository.Monitor;
|
||||
using Disco.Models.Repository;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace Disco.Services.Documents.ManagedGroups
|
||||
{
|
||||
public static class DocumentTemplateManagedGroups
|
||||
{
|
||||
internal static Lazy<IObservable<RepositoryMonitorEvent>> DeviceAttachmentAddRepositoryEvents;
|
||||
internal static Lazy<IObservable<RepositoryMonitorEvent>> JobAttachmentAddRepositoryEvents;
|
||||
internal static Lazy<IObservable<RepositoryMonitorEvent>> UserAttachmentAddRepositoryEvents;
|
||||
|
||||
internal static Lazy<Subject<Tuple<DiscoDataContext, int, string, string>>> DeviceAttachmentRemoveEvents;
|
||||
internal static Lazy<Subject<Tuple<DiscoDataContext, int, string, int>>> JobAttachmentRemoveEvents;
|
||||
internal static Lazy<Subject<Tuple<DiscoDataContext, int, string, string>>> UserAttachmentRemoveEvents;
|
||||
|
||||
internal static Lazy<IObservable<RepositoryMonitorEvent>> DeviceRenameRepositoryEvents;
|
||||
internal static Lazy<IObservable<RepositoryMonitorEvent>> JobCloseRepositoryEvents;
|
||||
internal static Lazy<IObservable<RepositoryMonitorEvent>> DeviceAssignmentRepositoryEvents;
|
||||
|
||||
static DocumentTemplateManagedGroups()
|
||||
{
|
||||
DeviceAttachmentAddRepositoryEvents =
|
||||
new Lazy<IObservable<RepositoryMonitorEvent>>(() =>
|
||||
RepositoryMonitor.StreamAfterCommit.Where(e =>
|
||||
e.EntityType == typeof(DeviceAttachment) &&
|
||||
((DeviceAttachment)e.Entity).DocumentTemplateId != null &&
|
||||
e.EventType == RepositoryMonitorEventType.Added
|
||||
));
|
||||
JobAttachmentAddRepositoryEvents =
|
||||
new Lazy<IObservable<RepositoryMonitorEvent>>(() =>
|
||||
RepositoryMonitor.StreamAfterCommit.Where(e =>
|
||||
e.EntityType == typeof(JobAttachment) &&
|
||||
((JobAttachment)e.Entity).DocumentTemplateId != null &&
|
||||
e.EventType == RepositoryMonitorEventType.Added
|
||||
));
|
||||
UserAttachmentAddRepositoryEvents =
|
||||
new Lazy<IObservable<RepositoryMonitorEvent>>(() =>
|
||||
RepositoryMonitor.StreamAfterCommit.Where(e =>
|
||||
e.EntityType == typeof(UserAttachment) &&
|
||||
((UserAttachment)e.Entity).DocumentTemplateId != null &&
|
||||
e.EventType == RepositoryMonitorEventType.Added
|
||||
));
|
||||
|
||||
DeviceAttachmentRemoveEvents =
|
||||
new Lazy<Subject<Tuple<DiscoDataContext, int, string, string>>>(() => new Subject<Tuple<DiscoDataContext, int, string, string>>());
|
||||
JobAttachmentRemoveEvents =
|
||||
new Lazy<Subject<Tuple<DiscoDataContext, int, string, int>>>(() => new Subject<Tuple<DiscoDataContext, int, string, int>>());
|
||||
UserAttachmentRemoveEvents =
|
||||
new Lazy<Subject<Tuple<DiscoDataContext, int, string, string>>>(() => new Subject<Tuple<DiscoDataContext, int, string, string>>());
|
||||
|
||||
DeviceRenameRepositoryEvents =
|
||||
new Lazy<IObservable<RepositoryMonitorEvent>>(() =>
|
||||
RepositoryMonitor.StreamBeforeCommit.Where(e =>
|
||||
e.EntityType == typeof(Device) &&
|
||||
e.EventType == RepositoryMonitorEventType.Modified &&
|
||||
e.ModifiedProperties.Contains("DeviceDomainId")
|
||||
));
|
||||
JobCloseRepositoryEvents =
|
||||
new Lazy<IObservable<RepositoryMonitorEvent>>(() =>
|
||||
RepositoryMonitor.StreamAfterCommit.Where(e =>
|
||||
e.EntityType == typeof(Job) &&
|
||||
(((Job)e.Entity).DeviceSerialNumber != null || ((Job)e.Entity).UserId != null) &&
|
||||
e.EventType == RepositoryMonitorEventType.Modified &&
|
||||
e.ModifiedProperties.Contains("ClosedDate")
|
||||
));
|
||||
DeviceAssignmentRepositoryEvents =
|
||||
new Lazy<IObservable<RepositoryMonitorEvent>>(() =>
|
||||
RepositoryMonitor.StreamBeforeCommit.Where(e =>
|
||||
e.EntityType == typeof(Device) &&
|
||||
e.EventType == RepositoryMonitorEventType.Modified &&
|
||||
e.ModifiedProperties.Contains("AssignedUserId")
|
||||
));
|
||||
}
|
||||
|
||||
public static void Initialize(DiscoDataContext Database)
|
||||
{
|
||||
Database.DocumentTemplates
|
||||
.Where(dp => dp.DevicesLinkedGroup != null || dp.UsersLinkedGroup != null)
|
||||
.ToList()
|
||||
.ForEach(dp =>
|
||||
{
|
||||
DocumentTemplateDevicesManagedGroup.Initialize(dp);
|
||||
DocumentTemplateUsersManagedGroup.Initialize(dp);
|
||||
});
|
||||
}
|
||||
|
||||
public static void TriggerDeviceAttachmentDeleted(DiscoDataContext Database, int AttachmentId, string DocumentTemplateId, string DeviceSerialNumber)
|
||||
{
|
||||
if (DocumentTemplateId != null)
|
||||
DeviceAttachmentRemoveEvents.Value.OnNext(Tuple.Create(Database, AttachmentId, DocumentTemplateId, DeviceSerialNumber));
|
||||
}
|
||||
|
||||
public static void TriggerJobAttachmentDeleted(DiscoDataContext Database, int AttachmentId, string DocumentTemplateId, int JobId)
|
||||
{
|
||||
if (DocumentTemplateId != null)
|
||||
JobAttachmentRemoveEvents.Value.OnNext(Tuple.Create(Database, AttachmentId, DocumentTemplateId, JobId));
|
||||
}
|
||||
|
||||
public static void TriggerUserAttachmentDeleted(DiscoDataContext Database, int AttachmentId, string DocumentTemplateId, string UserId)
|
||||
{
|
||||
if (DocumentTemplateId != null)
|
||||
UserAttachmentRemoveEvents.Value.OnNext(Tuple.Create(Database, AttachmentId, DocumentTemplateId, UserId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Data.Repository.Monitor;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Interop.ActiveDirectory;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace Disco.Services.Documents.ManagedGroups
|
||||
{
|
||||
public class DocumentTemplateUsersManagedGroup : ADManagedGroup
|
||||
{
|
||||
private const string KeyFormat = "DocumentTemplate_{0}_Users";
|
||||
private const string UserDescriptionFormat = "Users with a {0} attachment will be added to this Active Directory group.";
|
||||
private const string DescriptionFormat = "{0}s with a {1} attachment will have any associated users added to this Active Directory group.";
|
||||
private const string CategoryDescriptionFormat = "Related Users Linked Group";
|
||||
private const string GroupDescriptionFormat = "{0} [Document Template Users]";
|
||||
|
||||
private IDisposable repositoryAddSubscription;
|
||||
private IDisposable repositoryRemoveSubscription;
|
||||
private IDisposable jobCloseRepositorySubscription;
|
||||
private IDisposable deviceAssignmentRepositorySubscription;
|
||||
private string DocumentTemplateId;
|
||||
private string DocumentTemplateDescription;
|
||||
private string DocumentTemplateScope;
|
||||
|
||||
public override string Description { get { return GetDescription(DocumentTemplateScope, DocumentTemplateDescription); } }
|
||||
public override string CategoryDescription { get { return CategoryDescriptionFormat; } }
|
||||
public override string GroupDescription { get { return string.Format(GroupDescriptionFormat, DocumentTemplateDescription); } }
|
||||
public override bool IncludeFilterBeginDate { get { return true; } }
|
||||
|
||||
private DocumentTemplateUsersManagedGroup(string Key, ADManagedGroupConfiguration Configuration, DocumentTemplate DocumentTemplate)
|
||||
: base(Key, Configuration)
|
||||
{
|
||||
this.DocumentTemplateId = DocumentTemplate.Id;
|
||||
this.DocumentTemplateDescription = DocumentTemplate.Description;
|
||||
this.DocumentTemplateScope = DocumentTemplate.Scope;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
// Subscribe to changes
|
||||
switch (DocumentTemplateScope)
|
||||
{
|
||||
case DocumentTemplate.DocumentTemplateScopes.Device:
|
||||
// Observe Device Attachments
|
||||
repositoryAddSubscription = DocumentTemplateManagedGroups.DeviceAttachmentAddRepositoryEvents.Value
|
||||
.Where(e => ((DeviceAttachment)e.Entity).DocumentTemplateId == DocumentTemplateId)
|
||||
.Subscribe(ProcessDeviceAttachmentAddEvent);
|
||||
repositoryRemoveSubscription = DocumentTemplateManagedGroups.DeviceAttachmentRemoveEvents.Value
|
||||
.Where(e => e.Item3 == DocumentTemplateId)
|
||||
.Subscribe(ProcessDeviceAttachmentRemoveEvent);
|
||||
// Observe Device Assignments
|
||||
deviceAssignmentRepositorySubscription = DocumentTemplateManagedGroups.DeviceAssignmentRepositoryEvents.Value
|
||||
.Subscribe(ProcessDeviceAssignmentRepositoryEvent);
|
||||
break;
|
||||
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||
// Observe Job Attachments
|
||||
repositoryAddSubscription = DocumentTemplateManagedGroups.UserAttachmentAddRepositoryEvents.Value
|
||||
.Where(e => ((JobAttachment)e.Entity).DocumentTemplateId == DocumentTemplateId)
|
||||
.Subscribe(ProcessJobAttachmentAddEvent);
|
||||
repositoryRemoveSubscription = DocumentTemplateManagedGroups.JobAttachmentRemoveEvents.Value
|
||||
.Where(e => e.Item3 == DocumentTemplateId)
|
||||
.Subscribe(ProcessJobAttachmentRemoveEvent);
|
||||
// Observe Job Close/Reopen
|
||||
jobCloseRepositorySubscription = DocumentTemplateManagedGroups.JobCloseRepositoryEvents.Value
|
||||
.Subscribe(ProcessJobCloseRepositoryEvent);
|
||||
break;
|
||||
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||
// Observe User Attachments
|
||||
repositoryAddSubscription = DocumentTemplateManagedGroups.UserAttachmentAddRepositoryEvents.Value
|
||||
.Where(e => ((UserAttachment)e.Entity).DocumentTemplateId == DocumentTemplateId)
|
||||
.Subscribe(ProcessUserAttachmentAddEvent);
|
||||
repositoryRemoveSubscription = DocumentTemplateManagedGroups.UserAttachmentRemoveEvents.Value
|
||||
.Where(e => e.Item3 == DocumentTemplateId)
|
||||
.Subscribe(ProcessUserAttachmentRemoveEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetKey(DocumentTemplate DocumentTemplate)
|
||||
{
|
||||
return string.Format(KeyFormat, DocumentTemplate.Id);
|
||||
}
|
||||
private static string GetDescription(string DocumentTemplateScope, string DocumentTemplateDescription)
|
||||
{
|
||||
switch (DocumentTemplateScope)
|
||||
{
|
||||
case DocumentTemplate.DocumentTemplateScopes.Device:
|
||||
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||
return string.Format(DescriptionFormat, DocumentTemplateScope, DocumentTemplateDescription);
|
||||
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||
return string.Format(UserDescriptionFormat, DocumentTemplateDescription);
|
||||
default:
|
||||
throw new ArgumentException("Unknown Document Template Scope", "Scope");
|
||||
}
|
||||
}
|
||||
public static string GetDescription(DocumentTemplate DocumentTemplate)
|
||||
{
|
||||
return GetDescription(DocumentTemplate.Scope, DocumentTemplate.Description);
|
||||
}
|
||||
public static string GetCategoryDescription(DocumentTemplate DocumentTemplate)
|
||||
{
|
||||
return CategoryDescriptionFormat;
|
||||
}
|
||||
|
||||
public static bool TryGetManagedGroup(DocumentTemplate DocumentTemplate, out DocumentTemplateUsersManagedGroup ManagedGroup)
|
||||
{
|
||||
ADManagedGroup managedGroup;
|
||||
string key = GetKey(DocumentTemplate);
|
||||
|
||||
if (ActiveDirectory.Context.ManagedGroups.TryGetValue(key, out managedGroup))
|
||||
{
|
||||
ManagedGroup = (DocumentTemplateUsersManagedGroup)managedGroup;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ManagedGroup = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static DocumentTemplateUsersManagedGroup Initialize(DocumentTemplate Template)
|
||||
{
|
||||
var key = GetKey(Template);
|
||||
|
||||
if (!string.IsNullOrEmpty(Template.UsersLinkedGroup))
|
||||
{
|
||||
var config = ADManagedGroup.ConfigurationFromJson(Template.UsersLinkedGroup);
|
||||
|
||||
if (config != null && !string.IsNullOrWhiteSpace(config.GroupId))
|
||||
{
|
||||
var group = new DocumentTemplateUsersManagedGroup(
|
||||
key,
|
||||
config,
|
||||
Template);
|
||||
|
||||
// Add to AD Context
|
||||
ActiveDirectory.Context.ManagedGroups.AddOrUpdate(group);
|
||||
|
||||
return group;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from AD Context
|
||||
ActiveDirectory.Context.ManagedGroups.Remove(key);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> DetermineMembers(DiscoDataContext Database)
|
||||
{
|
||||
switch (DocumentTemplateScope)
|
||||
{
|
||||
case DocumentTemplate.DocumentTemplateScopes.Device:
|
||||
return Database.Devices
|
||||
.Where(d => d.AssignedUserId != null && d.DeviceAttachments.Any(a => a.DocumentTemplateId == this.DocumentTemplateId))
|
||||
.Select(d => d.AssignedUserId);
|
||||
case DocumentTemplate.DocumentTemplateScopes.Job:
|
||||
return Database.Jobs
|
||||
.Where(j => !j.ClosedDate.HasValue && j.UserId != null && j.JobAttachments.Any(a => a.DocumentTemplateId == this.DocumentTemplateId))
|
||||
.Select(j => j.UserId)
|
||||
.Distinct();
|
||||
case DocumentTemplate.DocumentTemplateScopes.User:
|
||||
return Database.Users
|
||||
.Where(u => u.UserAttachments.Any(a => a.DocumentTemplateId == this.DocumentTemplateId))
|
||||
.Select(u => u.UserId);
|
||||
default:
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
}
|
||||
|
||||
#region Device Scope
|
||||
private bool DeviceContainsAttachment(DiscoDataContext Database, string DeviceSerialNumber, out string UserId)
|
||||
{
|
||||
var result = Database.Devices
|
||||
.Where(d => d.SerialNumber == DeviceSerialNumber && d.AssignedUser != null)
|
||||
.Select(d => new Tuple<string, bool>(d.AssignedUserId, d.DeviceAttachments.Any(a => a.DocumentTemplateId == this.DocumentTemplateId)))
|
||||
.FirstOrDefault();
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
UserId = null;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
UserId = result.Item1;
|
||||
return result.Item2;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessDeviceAttachmentAddEvent(RepositoryMonitorEvent e)
|
||||
{
|
||||
var attachment = (DeviceAttachment)e.Entity;
|
||||
|
||||
string userId;
|
||||
if (DeviceContainsAttachment(e.Database, attachment.DeviceSerialNumber, out userId) && userId != null)
|
||||
AddMember(userId, (database) => new string[] { userId });
|
||||
}
|
||||
private void ProcessDeviceAttachmentRemoveEvent(Tuple<DiscoDataContext, int, string, string> e)
|
||||
{
|
||||
var deviceSerialNumber = e.Item4;
|
||||
string userId = e.Item1.Devices.Where(d => d.SerialNumber == deviceSerialNumber && d.AssignedUserId != null).Select(j => j.AssignedUserId).FirstOrDefault();
|
||||
|
||||
if (userId != null)
|
||||
{
|
||||
RemoveMember(userId, (database) =>
|
||||
{
|
||||
if (DeviceContainsAttachment(database, deviceSerialNumber, out userId) && userId != null)
|
||||
return new string[] { userId };
|
||||
else
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Job Scope
|
||||
private bool JobsContainAttachment(DiscoDataContext Database, int JobId, out string UserId)
|
||||
{
|
||||
var result = Database.Jobs
|
||||
.Where(j => j.Id == JobId && j.UserId != null)
|
||||
.Select(j => new Tuple<string, bool>(
|
||||
j.UserId,
|
||||
j.User.Jobs.Where(uj => !uj.ClosedDate.HasValue).Any(uj => uj.JobAttachments.Any(a => a.DocumentTemplateId == this.DocumentTemplateId)))
|
||||
).FirstOrDefault();
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
UserId = null;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
UserId = result.Item1;
|
||||
return result.Item2;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessJobAttachmentAddEvent(RepositoryMonitorEvent e)
|
||||
{
|
||||
var attachment = (JobAttachment)e.Entity;
|
||||
|
||||
string userId;
|
||||
if (JobsContainAttachment(e.Database, attachment.JobId, out userId) && userId != null)
|
||||
AddMember(userId, (database) => new string[] { userId });
|
||||
}
|
||||
private void ProcessJobAttachmentRemoveEvent(Tuple<DiscoDataContext, int, string, int> e)
|
||||
{
|
||||
var jobId = e.Item4;
|
||||
string userId = e.Item1.Jobs.Where(j => j.Id == jobId && j.UserId != null).Select(j => j.UserId).FirstOrDefault();
|
||||
|
||||
if (userId != null)
|
||||
{
|
||||
RemoveMember(userId, (database) =>
|
||||
{
|
||||
if (JobsContainAttachment(database, jobId, out userId) && userId != null)
|
||||
return new string[] { userId };
|
||||
else
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region User Scope
|
||||
private bool UserContainAttachment(DiscoDataContext Database, string UserId)
|
||||
{
|
||||
var result = Database.Users
|
||||
.Where(u => u.UserId == UserId)
|
||||
.Any(u => u.UserAttachments.Any(a => a.DocumentTemplateId == this.DocumentTemplateId));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void ProcessUserAttachmentAddEvent(RepositoryMonitorEvent e)
|
||||
{
|
||||
var attachment = (UserAttachment)e.Entity;
|
||||
var userId = attachment.UserId;
|
||||
|
||||
if (UserContainAttachment(e.Database, userId) && userId != null)
|
||||
AddMember(userId, (database) => new string[] { userId });
|
||||
}
|
||||
private void ProcessUserAttachmentRemoveEvent(Tuple<DiscoDataContext, int, string, string> e)
|
||||
{
|
||||
var userId = e.Item4;
|
||||
|
||||
RemoveMember(userId, (database) =>
|
||||
{
|
||||
if (!UserContainAttachment(database, userId))
|
||||
return new string[] { userId };
|
||||
else
|
||||
return null;
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void ProcessJobCloseRepositoryEvent(RepositoryMonitorEvent e)
|
||||
{
|
||||
var job = (Job)e.Entity;
|
||||
|
||||
if (job.UserId != null)
|
||||
{
|
||||
var jobId = job.Id;
|
||||
|
||||
var relevantJob = e.Database.Jobs
|
||||
.Where(j => j.Id == jobId && j.JobAttachments.Any(ja => ja.DocumentTemplateId == this.DocumentTemplateId))
|
||||
.Any();
|
||||
|
||||
if (relevantJob)
|
||||
{
|
||||
string userId;
|
||||
if (JobsContainAttachment(e.Database, jobId, out userId))
|
||||
AddMember(userId, (database) => new string[] { userId });
|
||||
else
|
||||
RemoveMember(userId, (database) => new string[] { userId });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessDeviceAssignmentRepositoryEvent(RepositoryMonitorEvent Event)
|
||||
{
|
||||
var device = (Device)Event.Entity;
|
||||
var deviceSerialNumber = device.SerialNumber;
|
||||
|
||||
var relevantDevice = Event.Database.Devices
|
||||
.Where(d => d.SerialNumber == deviceSerialNumber && d.DeviceAttachments.Any(ja => ja.DocumentTemplateId == this.DocumentTemplateId))
|
||||
.Any();
|
||||
|
||||
if (relevantDevice)
|
||||
{
|
||||
var deviceCurrentAssignedUserId = device.AssignedUserId;
|
||||
var devicePreviousAssignedUserId = Event.GetPreviousPropertyValue<string>("AssignedUserId");
|
||||
|
||||
Event.ExecuteAfterCommit(e =>
|
||||
{
|
||||
if (devicePreviousAssignedUserId != null)
|
||||
RemoveMember(devicePreviousAssignedUserId, (database) => new string[] { devicePreviousAssignedUserId });
|
||||
|
||||
if (deviceCurrentAssignedUserId != null)
|
||||
AddMember(deviceCurrentAssignedUserId, (database) => new string[] { deviceCurrentAssignedUserId });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (repositoryAddSubscription != null)
|
||||
repositoryAddSubscription.Dispose();
|
||||
|
||||
if (repositoryRemoveSubscription != null)
|
||||
repositoryRemoveSubscription.Dispose();
|
||||
|
||||
if (jobCloseRepositorySubscription != null)
|
||||
jobCloseRepositorySubscription.Dispose();
|
||||
|
||||
if (deviceAssignmentRepositorySubscription != null)
|
||||
deviceAssignmentRepositorySubscription.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
public class ADGroup : IADObject
|
||||
{
|
||||
internal static readonly string[] LoadProperties = { "name", "distinguishedName", "sAMAccountName", "objectSid", "memberOf" };
|
||||
internal static string LdapSearchFilterTemplate = "(&(objectCategory=Group)(|(sAMAccountName=*{0}*)(name=*{0}*)(cn=*{0}*)))";
|
||||
internal static 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}))";
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
public class ADUserAccount : IADObject
|
||||
{
|
||||
internal const string LdapSamAccountNameFilterTemplate = "(&(objectCategory=Person)(sAMAccountName={0}))";
|
||||
internal static string LdapSearchFilterTemplate = "(&(objectCategory=Person)(objectClass=user)(|(sAMAccountName=*{0}*)(displayName=*{0}*)(sn=*{0}*)(givenName=*{0}*)))";
|
||||
internal static string LdapSearchFilterTemplate = "(&(objectCategory=Person)(objectClass=user)(|(sAMAccountName={0}*)(displayName={0}*)(sn={0}*)(givenName={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" };
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
}
|
||||
}
|
||||
|
||||
#region Contructor/Initializing
|
||||
#region Constructor/Initializing
|
||||
|
||||
private ActiveDirectoryContext()
|
||||
{
|
||||
@@ -56,11 +56,7 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
this._SearchAllForestServers = Database.DiscoConfiguration.ActiveDirectory.SearchAllForestServers ?? true;
|
||||
|
||||
// Set Search LDAP Filters
|
||||
if (Database.DiscoConfiguration.ActiveDirectory.SearchWildcardSuffixOnly)
|
||||
{
|
||||
ADGroup.LdapSearchFilterTemplate = "(&(objectCategory=Group)(|(sAMAccountName={0}*)(name={0}*)(cn={0}*)))";
|
||||
ADUserAccount.LdapSearchFilterTemplate = "(&(objectCategory=Person)(objectClass=user)(|(sAMAccountName={0}*)(displayName={0}*)(sn={0}*)(givenName={0}*)))";
|
||||
}
|
||||
InitializeWildcardSearchSufixOnly(Database.DiscoConfiguration.ActiveDirectory.SearchWildcardSuffixOnly);
|
||||
|
||||
// Determine Site
|
||||
var computerSite = ActiveDirectorySite.GetComputerSite();
|
||||
@@ -235,6 +231,26 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
|
||||
#region Configuration
|
||||
|
||||
public void UpdateWildcardSearchSuffixOnly(DiscoDataContext Database, bool SearchWildcardSuffixOnly)
|
||||
{
|
||||
Database.DiscoConfiguration.ActiveDirectory.SearchWildcardSuffixOnly = SearchWildcardSuffixOnly;
|
||||
InitializeWildcardSearchSufixOnly(SearchWildcardSuffixOnly);
|
||||
}
|
||||
|
||||
private void InitializeWildcardSearchSufixOnly(bool SearchWildcardSuffixOnly)
|
||||
{
|
||||
if (SearchWildcardSuffixOnly)
|
||||
{
|
||||
ADGroup.LdapSearchFilterTemplate = "(&(objectCategory=Group)(|(sAMAccountName={0}*)(name={0}*)(cn={0}*)))";
|
||||
ADUserAccount.LdapSearchFilterTemplate = "(&(objectCategory=Person)(objectClass=user)(|(sAMAccountName={0}*)(displayName={0}*)(sn={0}*)(givenName={0}*)))";
|
||||
}
|
||||
else
|
||||
{
|
||||
ADGroup.LdapSearchFilterTemplate = "(&(objectCategory=Group)(|(sAMAccountName=*{0}*)(name=*{0}*)(cn=*{0}*)))";
|
||||
ADUserAccount.LdapSearchFilterTemplate = "(&(objectCategory=Person)(objectClass=user)(|(sAMAccountName=*{0}*)(displayName=*{0}*)(sn=*{0}*)(givenName=*{0}*)))";
|
||||
}
|
||||
}
|
||||
|
||||
public bool UpdateSearchAllForestServers(DiscoDataContext Database, bool SearchAllForestServers)
|
||||
{
|
||||
if (SearchAllForestServers == false)
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace Disco.Services.Interop.DiscoServices
|
||||
}
|
||||
}
|
||||
|
||||
var manifestJson = JsonConvert.SerializeObject(result);
|
||||
var manifestJson = JsonConvert.SerializeObject(result, Formatting.Indented);
|
||||
|
||||
var manifestFile = PluginLibrary.ManifestFilename(Database);
|
||||
|
||||
|
||||
@@ -0,0 +1,725 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.BI.Config;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Plugins;
|
||||
using Disco.Services.Plugins.Features.RepairProvider;
|
||||
using Disco.Services.Plugins.Features.WarrantyProvider;
|
||||
using Disco.Services.Users;
|
||||
using Exceptionless;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using DiscoServicesJobs = Disco.Services.Interop.DiscoServices.Jobs;
|
||||
using PublishJobResult = Disco.Models.Services.Interop.DiscoServices.PublishJobResult;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class JobActionExtensions
|
||||
{
|
||||
|
||||
#region Create
|
||||
public static bool CanCreate()
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Job.Actions.Create))
|
||||
return false;
|
||||
|
||||
if (!UserService.CurrentAuthorization.HasAny(Claims.Job.Types.CreateHMisc, Claims.Job.Types.CreateHNWar, Claims.Job.Types.CreateHWar, Claims.Job.Types.CreateSApp, Claims.Job.Types.CreateSImg, Claims.Job.Types.CreateSOS, Claims.Job.Types.CreateUMgmt))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Device Held
|
||||
public static bool CanDeviceHeld(this Job j)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Job.Properties.DeviceHeld))
|
||||
return false;
|
||||
|
||||
return (!j.ClosedDate.HasValue) && (j.DeviceSerialNumber != null) &&
|
||||
(!j.DeviceHeld.HasValue || j.DeviceReturnedDate.HasValue);
|
||||
}
|
||||
public static void OnDeviceHeld(this Job j, User Technician)
|
||||
{
|
||||
if (!j.CanDeviceHeld())
|
||||
throw new InvalidOperationException("Holding Device was Denied");
|
||||
|
||||
j.DeviceHeld = DateTime.Now;
|
||||
j.DeviceHeldTechUserId = Technician.UserId;
|
||||
j.DeviceReadyForReturn = null;
|
||||
j.DeviceReadyForReturnTechUserId = null;
|
||||
j.DeviceReturnedDate = null;
|
||||
j.DeviceReturnedTechUserId = null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Device Ready for Return
|
||||
public static bool CanDeviceReadyForReturn(this Job j)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Job.Properties.DeviceReadyForReturn))
|
||||
return false;
|
||||
|
||||
return (!j.ClosedDate.HasValue) && j.DeviceHeld.HasValue &&
|
||||
!j.DeviceReadyForReturn.HasValue && !j.DeviceReturnedDate.HasValue;
|
||||
}
|
||||
public static void OnDeviceReadyForReturn(this Job j, User Technician)
|
||||
{
|
||||
if (!j.CanDeviceReadyForReturn())
|
||||
throw new InvalidOperationException("Device Ready for Return was Denied");
|
||||
|
||||
j.DeviceReadyForReturn = DateTime.Now;
|
||||
j.DeviceReadyForReturnTechUserId = Technician.UserId;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Device Returned
|
||||
public static bool CanDeviceReturned(this Job j)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Job.Properties.DeviceReturned))
|
||||
return false;
|
||||
|
||||
return (!j.ClosedDate.HasValue) && j.DeviceHeld.HasValue &&
|
||||
!j.DeviceReturnedDate.HasValue;
|
||||
}
|
||||
public static void OnDeviceReturned(this Job j, User Technician)
|
||||
{
|
||||
if (!j.CanDeviceReturned())
|
||||
throw new InvalidOperationException("Device Return was Denied");
|
||||
|
||||
j.DeviceReturnedDate = DateTime.Now;
|
||||
j.DeviceReturnedTechUserId = Technician.UserId;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Waiting For User Action
|
||||
public static bool CanWaitingForUserAction(this Job j)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Job.Properties.WaitingForUserAction))
|
||||
return false;
|
||||
|
||||
return !j.ClosedDate.HasValue && (j.UserId != null) && !j.WaitingForUserAction.HasValue;
|
||||
}
|
||||
public static void OnWaitingForUserAction(this Job j, DiscoDataContext Database, User Technician, string Reason)
|
||||
{
|
||||
if (!j.CanWaitingForUserAction())
|
||||
throw new InvalidOperationException("Waiting for User Action was Denied");
|
||||
|
||||
j.WaitingForUserAction = DateTime.Now;
|
||||
|
||||
// Write Log
|
||||
JobLog jobLog = new JobLog()
|
||||
{
|
||||
JobId = j.Id,
|
||||
TechUserId = Technician.UserId,
|
||||
Timestamp = DateTime.Now,
|
||||
Comments = string.Format("# Waiting on User Action\r\n{0}", string.IsNullOrWhiteSpace(Reason) ? "<no reason provided>" : Reason)
|
||||
};
|
||||
Database.JobLogs.Add(jobLog);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Not Waiting For User Action
|
||||
public static bool CanNotWaitingForUserAction(this Job j)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Job.Properties.NotWaitingForUserAction))
|
||||
return false;
|
||||
|
||||
return j.WaitingForUserAction.HasValue;
|
||||
}
|
||||
public static void OnNotWaitingForUserAction(this Job j, DiscoDataContext Database, User Technician, string Resolution)
|
||||
{
|
||||
if (!j.CanNotWaitingForUserAction())
|
||||
throw new InvalidOperationException("Not Waiting for User Action was Denied");
|
||||
|
||||
j.WaitingForUserAction = null;
|
||||
|
||||
// Write Log
|
||||
JobLog jobLog = new JobLog()
|
||||
{
|
||||
JobId = j.Id,
|
||||
TechUserId = Technician.UserId,
|
||||
Timestamp = DateTime.Now,
|
||||
Comments = string.Format("# User Action Resolved\r\n{0}", string.IsNullOrWhiteSpace(Resolution) ? "<no comment provided>" : Resolution)
|
||||
};
|
||||
Database.JobLogs.Add(jobLog);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Log Warranty
|
||||
public static bool CanLogWarranty(this Job j)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Job.Actions.LogWarranty))
|
||||
return false;
|
||||
|
||||
return !j.ClosedDate.HasValue &&
|
||||
(j.DeviceSerialNumber != null) &&
|
||||
j.JobTypeId == JobType.JobTypeIds.HWar &&
|
||||
!j.JobMetaWarranty.ExternalLoggedDate.HasValue;
|
||||
}
|
||||
public static void OnLogWarranty(this Job j, DiscoDataContext Database, string FaultDescription, List<JobAttachment> SendAttachments, PluginFeatureManifest WarrantyProviderDefinition, OrganisationAddress Address, User TechUser, Dictionary<string, string> WarrantyProviderProperties)
|
||||
{
|
||||
if (!j.CanLogWarranty())
|
||||
throw new InvalidOperationException("Log Warranty was Denied");
|
||||
|
||||
PublishJobResult publishJobResult = null;
|
||||
|
||||
using (WarrantyProviderFeature WarrantyProvider = WarrantyProviderDefinition.CreateInstance<WarrantyProviderFeature>())
|
||||
{
|
||||
if (SendAttachments != null && SendAttachments.Count > 0)
|
||||
{
|
||||
publishJobResult = DiscoServicesJobs.Publish(
|
||||
Database,
|
||||
j,
|
||||
TechUser,
|
||||
WarrantyProvider.WarrantyProviderId,
|
||||
null,
|
||||
FaultDescription,
|
||||
SendAttachments,
|
||||
AttachmentDataStoreExtensions.RepositoryFilename);
|
||||
|
||||
if (!publishJobResult.Success)
|
||||
throw new Exception(string.Format("Disco ICT Online Services failed with the following message: ", publishJobResult.ErrorMessage));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(FaultDescription))
|
||||
FaultDescription = publishJobResult.PublishMessage;
|
||||
else
|
||||
FaultDescription = string.Concat(FaultDescription, Environment.NewLine, "___", Environment.NewLine, publishJobResult.PublishMessage);
|
||||
}
|
||||
|
||||
string submitDescription;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(FaultDescription))
|
||||
submitDescription = j.GenerateFaultDescriptionFooter(Database, WarrantyProviderDefinition);
|
||||
else
|
||||
submitDescription = string.Concat(FaultDescription, Environment.NewLine, Environment.NewLine, j.GenerateFaultDescriptionFooter(Database, WarrantyProviderDefinition));
|
||||
|
||||
string providerRef = WarrantyProvider.SubmitJob(Database, j, Address, TechUser, submitDescription, WarrantyProviderProperties);
|
||||
|
||||
j.JobMetaWarranty.ExternalLoggedDate = DateTime.Now;
|
||||
j.JobMetaWarranty.ExternalName = WarrantyProvider.WarrantyProviderId;
|
||||
|
||||
if (providerRef != null && providerRef.Length > 100)
|
||||
j.JobMetaWarranty.ExternalReference = providerRef.Substring(0, 100);
|
||||
else
|
||||
j.JobMetaWarranty.ExternalReference = providerRef;
|
||||
|
||||
// Write Log
|
||||
JobLog jobLog = new JobLog()
|
||||
{
|
||||
JobId = j.Id,
|
||||
TechUserId = TechUser.UserId,
|
||||
Timestamp = DateTime.Now,
|
||||
Comments = string.Format("# Warranty Claim Submitted\r\nProvider: **{0}**\r\nAddress: **{1}**\r\nReference: **{2}**\r\n___\r\n```{3}```", WarrantyProvider.Manifest.Name, Address.Name, providerRef, FaultDescription)
|
||||
};
|
||||
Database.JobLogs.Add(jobLog);
|
||||
|
||||
if (publishJobResult != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
DiscoServicesJobs.UpdateRecipientReference(Database, j, publishJobResult.Id, publishJobResult.Secret, j.JobMetaWarranty.ExternalReference);
|
||||
}
|
||||
catch (Exception ex) { ex.ToExceptionless().Submit(); } // Ignore Errors as this is not completely necessary
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void OnLogWarranty(this Job j, DiscoDataContext Database, string FaultDescription, string ManualProviderName, string ManualProviderReference, OrganisationAddress Address, User TechUser)
|
||||
{
|
||||
if (!j.CanLogWarranty())
|
||||
throw new InvalidOperationException("Log Warranty was Denied");
|
||||
|
||||
j.JobMetaWarranty.ExternalLoggedDate = DateTime.Now;
|
||||
j.JobMetaWarranty.ExternalName = ManualProviderName;
|
||||
|
||||
if (ManualProviderReference != null && ManualProviderReference.Length > 100)
|
||||
j.JobMetaWarranty.ExternalReference = ManualProviderReference.Substring(0, 100);
|
||||
else
|
||||
j.JobMetaWarranty.ExternalReference = ManualProviderReference;
|
||||
|
||||
// Write Log
|
||||
JobLog jobLog = new JobLog()
|
||||
{
|
||||
JobId = j.Id,
|
||||
TechUserId = TechUser.UserId,
|
||||
Timestamp = DateTime.Now,
|
||||
Comments = string.Format("# Manual Warranty Claim Submitted\r\nProvider: **{0}**\r\nAddress: **{1}**\r\nReference: **{2}**\r\n___\r\n```{3}```", ManualProviderName, Address.Name, ManualProviderReference ?? "<none>", FaultDescription)
|
||||
};
|
||||
Database.JobLogs.Add(jobLog);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Convert HWar to HNWar
|
||||
public static bool CanConvertHWarToHNWar(this Job j)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Job.Actions.ConvertHWarToHNWar))
|
||||
return false;
|
||||
|
||||
return !j.ClosedDate.HasValue && (j.DeviceSerialNumber != null) &&
|
||||
j.JobTypeId == JobType.JobTypeIds.HWar && string.IsNullOrEmpty(j.JobMetaWarranty.ExternalReference);
|
||||
}
|
||||
public static void OnConvertHWarToHNWar(this Job j, DiscoDataContext Database)
|
||||
{
|
||||
if (!j.CanConvertHWarToHNWar())
|
||||
throw new InvalidOperationException("Convert HWar to HNWar was Denied");
|
||||
|
||||
var techUser = UserService.CurrentUser;
|
||||
|
||||
// Remove JobMetaWarranty
|
||||
if (j.JobMetaWarranty != null)
|
||||
Database.JobMetaWarranties.Remove(j.JobMetaWarranty);
|
||||
|
||||
// Add JobMetaNonWarranty
|
||||
var metaHNWar = new JobMetaNonWarranty() { Job = j };
|
||||
Database.JobMetaNonWarranties.Add(metaHNWar);
|
||||
|
||||
// Swap Job Sub Types
|
||||
List<string> jobSubTypes = j.JobSubTypes.Select(jst => jst.Id).ToList();
|
||||
j.JobSubTypes.Clear();
|
||||
foreach (var jst in Database.JobSubTypes.Where(i => i.JobTypeId == JobType.JobTypeIds.HNWar && jobSubTypes.Contains(i.Id)))
|
||||
j.JobSubTypes.Add(jst);
|
||||
|
||||
// Add Components
|
||||
var components = Database.DeviceComponents.Include("JobSubTypes").Where(c => !c.DeviceModelId.HasValue || c.DeviceModelId == j.Device.DeviceModelId);
|
||||
var jobComponents = new List<DeviceComponent>();
|
||||
foreach (var component in components)
|
||||
{
|
||||
if (component.JobSubTypes.Count == 0)
|
||||
{
|
||||
jobComponents.Add(component);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var st in component.JobSubTypes)
|
||||
{
|
||||
foreach (var jst in j.JobSubTypes)
|
||||
{
|
||||
if (st.JobTypeId == jst.JobTypeId && st.Id == jst.Id)
|
||||
{
|
||||
jobComponents.Add(component);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (jobComponents.Contains(component))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var component in jobComponents)
|
||||
{
|
||||
Database.JobComponents.Add(new JobComponent()
|
||||
{
|
||||
Job = j,
|
||||
TechUserId = techUser.UserId,
|
||||
Cost = component.Cost,
|
||||
Description = component.Description
|
||||
});
|
||||
}
|
||||
|
||||
// Write Log
|
||||
JobLog jobLog = new JobLog()
|
||||
{
|
||||
JobId = j.Id,
|
||||
TechUserId = techUser.UserId,
|
||||
Timestamp = DateTime.Now,
|
||||
Comments = string.Format("# Job Type Converted\r\nFrom: **{0}**\r\nTo: **{1}**", Database.JobTypes.Find(JobType.JobTypeIds.HWar), Database.JobTypes.Find(JobType.JobTypeIds.HNWar))
|
||||
};
|
||||
Database.JobLogs.Add(jobLog);
|
||||
|
||||
j.JobTypeId = JobType.JobTypeIds.HNWar;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Warranty Completed
|
||||
public static bool CanWarrantyCompleted(this Job j)
|
||||
{
|
||||
return (j.JobTypeId == JobType.JobTypeIds.HWar) &&
|
||||
j.JobMetaWarranty.ExternalLoggedDate.HasValue &&
|
||||
!j.JobMetaWarranty.ExternalCompletedDate.HasValue;
|
||||
}
|
||||
public static void OnWarrantyCompleted(this Job j)
|
||||
{
|
||||
if (!j.CanWarrantyCompleted())
|
||||
throw new InvalidOperationException("Warranty Completed was Denied");
|
||||
|
||||
j.JobMetaWarranty.ExternalCompletedDate = DateTime.Now;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Insurance Claim Form Sent
|
||||
public static bool CanInsuranceClaimFormSent(this Job j)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Job.Properties.NonWarrantyProperties.InsuranceClaimFormSent))
|
||||
return false;
|
||||
|
||||
return (j.JobTypeId == JobType.JobTypeIds.HNWar) &&
|
||||
j.JobMetaNonWarranty.IsInsuranceClaim &&
|
||||
!j.JobMetaInsurance.ClaimFormSentDate.HasValue;
|
||||
}
|
||||
public static void OnInsuranceClaimFormSent(this Job j)
|
||||
{
|
||||
if (!j.CanInsuranceClaimFormSent())
|
||||
throw new InvalidOperationException("Insurance Claim Form Sent was Denied");
|
||||
|
||||
var techUser = UserService.CurrentUser;
|
||||
|
||||
j.JobMetaInsurance.ClaimFormSentDate = DateTime.Now;
|
||||
j.JobMetaInsurance.ClaimFormSentUserId = techUser.UserId;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Log Repair
|
||||
public static bool CanLogRepair(this Job j)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Job.Actions.LogRepair))
|
||||
return false;
|
||||
|
||||
return (j.JobTypeId == JobType.JobTypeIds.HNWar) &&
|
||||
(j.DeviceSerialNumber != null) &&
|
||||
!j.JobMetaNonWarranty.RepairerLoggedDate.HasValue &&
|
||||
!j.JobMetaNonWarranty.RepairerCompletedDate.HasValue;
|
||||
}
|
||||
public static void OnLogRepair(this Job j, DiscoDataContext Database, string RepairDescription, List<JobAttachment> SendAttachments, PluginFeatureManifest RepairProviderDefinition, OrganisationAddress Address, User TechUser, Dictionary<string, string> RepairProviderProperties)
|
||||
{
|
||||
if (!j.CanLogRepair())
|
||||
throw new InvalidOperationException("Log Repair was Denied");
|
||||
|
||||
PublishJobResult publishJobResult = null;
|
||||
|
||||
using (RepairProviderFeature RepairProvider = RepairProviderDefinition.CreateInstance<RepairProviderFeature>())
|
||||
{
|
||||
if (SendAttachments != null && SendAttachments.Count > 0)
|
||||
{
|
||||
publishJobResult = DiscoServicesJobs.Publish(
|
||||
Database,
|
||||
j,
|
||||
TechUser,
|
||||
RepairProvider.ProviderId,
|
||||
null,
|
||||
RepairDescription,
|
||||
SendAttachments,
|
||||
AttachmentDataStoreExtensions.RepositoryFilename);
|
||||
|
||||
if (!publishJobResult.Success)
|
||||
throw new Exception(string.Format("Disco ICT Online Services failed with the following message: ", publishJobResult.ErrorMessage));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(RepairDescription))
|
||||
RepairDescription = publishJobResult.PublishMessage;
|
||||
else
|
||||
RepairDescription = string.Concat(RepairDescription, Environment.NewLine, "___", Environment.NewLine, publishJobResult.PublishMessage);
|
||||
}
|
||||
|
||||
string submitDescription;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(RepairDescription))
|
||||
submitDescription = j.GenerateFaultDescriptionFooter(Database, RepairProviderDefinition);
|
||||
else
|
||||
submitDescription = string.Concat(RepairDescription, Environment.NewLine, Environment.NewLine, j.GenerateFaultDescriptionFooter(Database, RepairProviderDefinition));
|
||||
|
||||
string providerRef = RepairProvider.SubmitJob(Database, j, Address, TechUser, submitDescription, RepairProviderProperties);
|
||||
|
||||
j.JobMetaNonWarranty.RepairerLoggedDate = DateTime.Now;
|
||||
j.JobMetaNonWarranty.RepairerName = RepairProvider.ProviderId;
|
||||
|
||||
if (providerRef != null && providerRef.Length > 100)
|
||||
j.JobMetaNonWarranty.RepairerReference = providerRef.Substring(0, 100);
|
||||
else
|
||||
j.JobMetaNonWarranty.RepairerReference = providerRef;
|
||||
|
||||
// Write Log
|
||||
JobLog jobLog = new JobLog()
|
||||
{
|
||||
JobId = j.Id,
|
||||
TechUserId = TechUser.UserId,
|
||||
Timestamp = DateTime.Now,
|
||||
Comments = string.Format("# Repair Request Submitted\r\nProvider: **{0}**\r\nAddress: **{1}**\r\nReference: **{2}**\r\n___\r\n```{3}```", RepairProvider.Manifest.Name, Address.Name, providerRef, RepairDescription)
|
||||
};
|
||||
Database.JobLogs.Add(jobLog);
|
||||
|
||||
if (publishJobResult != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
DiscoServicesJobs.UpdateRecipientReference(Database, j, publishJobResult.Id, publishJobResult.Secret, j.JobMetaNonWarranty.RepairerReference);
|
||||
}
|
||||
catch (Exception ex) { ex.ToExceptionless().Submit(); } // Ignore Errors as this is not completely necessary
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void OnLogRepair(this Job j, DiscoDataContext Database, string FaultDescription, string ManualProviderName, string ManualProviderReference, OrganisationAddress Address, User TechUser)
|
||||
{
|
||||
if (!j.CanLogRepair())
|
||||
throw new InvalidOperationException("Log Repair was Denied");
|
||||
|
||||
j.JobMetaNonWarranty.RepairerLoggedDate = DateTime.Now;
|
||||
j.JobMetaNonWarranty.RepairerName = ManualProviderName;
|
||||
|
||||
if (ManualProviderReference != null && ManualProviderReference.Length > 100)
|
||||
j.JobMetaNonWarranty.RepairerReference = ManualProviderReference.Substring(0, 100);
|
||||
else
|
||||
j.JobMetaNonWarranty.RepairerReference = ManualProviderReference;
|
||||
|
||||
// Write Log
|
||||
JobLog jobLog = new JobLog()
|
||||
{
|
||||
JobId = j.Id,
|
||||
TechUserId = TechUser.UserId,
|
||||
Timestamp = DateTime.Now,
|
||||
Comments = string.Format("# Manual Repair Request Submitted\r\nProvider: **{0}**\r\nAddress: **{1}**\r\nReference: **{2}**\r\n___\r\n```{3}```", ManualProviderName, Address.Name, ManualProviderReference ?? "<none>", FaultDescription)
|
||||
};
|
||||
Database.JobLogs.Add(jobLog);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Repair Complete
|
||||
public static bool CanRepairComplete(this Job j)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Job.Properties.NonWarrantyProperties.RepairerCompletedDate))
|
||||
return false;
|
||||
|
||||
return (j.JobTypeId == JobType.JobTypeIds.HNWar) &&
|
||||
j.JobMetaNonWarranty.RepairerLoggedDate.HasValue &&
|
||||
!j.JobMetaNonWarranty.RepairerCompletedDate.HasValue;
|
||||
}
|
||||
public static void OnRepairComplete(this Job j)
|
||||
{
|
||||
if (!j.CanRepairComplete())
|
||||
throw new InvalidOperationException("Repair Complete was Denied");
|
||||
|
||||
j.JobMetaNonWarranty.RepairerCompletedDate = DateTime.Now;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Close
|
||||
public static void OnCloseNormally(this Job j, User Technician)
|
||||
{
|
||||
if (!j.CanCloseNormally())
|
||||
throw new InvalidOperationException("Close was Denied");
|
||||
|
||||
j.ClosedDate = DateTime.Now;
|
||||
j.ClosedTechUserId = Technician.UserId;
|
||||
}
|
||||
|
||||
private static bool CanCloseNever(this Job j, JobQueueJob IgnoreJobQueueJob = null)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Job.Actions.Close))
|
||||
return true;
|
||||
|
||||
if (j.ClosedDate.HasValue)
|
||||
return true; // Job already Closed
|
||||
|
||||
if (j.DeviceHeld.HasValue && !j.DeviceReturnedDate.HasValue)
|
||||
return true; // Device not returned to User
|
||||
|
||||
if (j.WaitingForUserAction.HasValue)
|
||||
return true; // Job waiting on User Action
|
||||
|
||||
if (j.JobQueues != null)
|
||||
{
|
||||
if (IgnoreJobQueueJob == null)
|
||||
{
|
||||
if (j.JobQueues.Any(jqj => !jqj.RemovedDate.HasValue))
|
||||
return true; // Job associated with a Job Queue
|
||||
}
|
||||
else
|
||||
{
|
||||
if (j.JobQueues.Any(jqj => jqj.Id != IgnoreJobQueueJob.Id && !jqj.RemovedDate.HasValue))
|
||||
return true; // Job associated with a Job Queue
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool CanCloseNormally(this Job j)
|
||||
{
|
||||
if (j.CanCloseNever())
|
||||
return false;
|
||||
|
||||
return j.CanCloseNormallyInternal();
|
||||
}
|
||||
|
||||
private static bool CanCloseNormallyInternal(this Job j)
|
||||
{
|
||||
switch (j.JobTypeId)
|
||||
{
|
||||
case JobType.JobTypeIds.HWar:
|
||||
|
||||
if (!string.IsNullOrEmpty(j.JobMetaWarranty.ExternalReference) && !j.JobMetaWarranty.ExternalCompletedDate.HasValue)
|
||||
return false; // Job Logged (Warranty) but not completed
|
||||
|
||||
break;
|
||||
case JobType.JobTypeIds.HNWar:
|
||||
|
||||
if (j.JobMetaNonWarranty.RepairerLoggedDate.HasValue && !j.JobMetaNonWarranty.RepairerCompletedDate.HasValue)
|
||||
return false; // Job Logged (Repair) but not completed
|
||||
|
||||
if (j.JobMetaNonWarranty.AccountingChargeRequiredDate.HasValue && !j.JobMetaNonWarranty.AccountingChargeAddedDate.HasValue)
|
||||
return false; // Accounting Charge Required, but not added
|
||||
|
||||
if ((j.JobMetaNonWarranty.AccountingChargeRequiredDate.HasValue || j.JobMetaNonWarranty.AccountingChargeAddedDate.HasValue) && !j.JobMetaNonWarranty.AccountingChargePaidDate.HasValue)
|
||||
return false; // Accounting Charge Required or Added, but not paid
|
||||
|
||||
if (j.JobMetaNonWarranty.IsInsuranceClaim && !j.JobMetaInsurance.ClaimFormSentDate.HasValue)
|
||||
return false; // Is Insurance Claim, but claim form not sent
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool CanCloseJobNormallyAfterRemoved(this JobQueueJob jqj)
|
||||
{
|
||||
if (jqj.Job.CanCloseNever(jqj))
|
||||
return false;
|
||||
|
||||
return jqj.Job.CanCloseNormallyInternal();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Force Close
|
||||
public static bool CanCloseForced(this Job j)
|
||||
{
|
||||
List<string> reasons;
|
||||
|
||||
return CanCloseForced(j, out reasons);
|
||||
}
|
||||
public static bool CanCloseForced(this Job j, out List<string> Reasons)
|
||||
{
|
||||
Reasons = null;
|
||||
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Job.Actions.ForceClose))
|
||||
return false;
|
||||
|
||||
if (j.CanCloseNever())
|
||||
return false;
|
||||
|
||||
if (j.CanCloseNormally())
|
||||
return false;
|
||||
|
||||
Reasons = new List<string>();
|
||||
|
||||
switch (j.JobTypeId)
|
||||
{
|
||||
case JobType.JobTypeIds.HWar:
|
||||
if (!string.IsNullOrEmpty(j.JobMetaWarranty.ExternalReference) && !j.JobMetaWarranty.ExternalCompletedDate.HasValue)
|
||||
Reasons.Add("Warranty Job Not Completed"); // Job Logged (Warranty) but not completed
|
||||
break;
|
||||
case JobType.JobTypeIds.HNWar:
|
||||
|
||||
if (j.JobMetaNonWarranty.RepairerLoggedDate.HasValue && !j.JobMetaNonWarranty.RepairerCompletedDate.HasValue)
|
||||
Reasons.Add("Repair Job Not Completed"); // Job Logged (Repair) but not completed
|
||||
|
||||
if (j.JobMetaNonWarranty.AccountingChargeRequiredDate.HasValue && (!j.JobMetaNonWarranty.AccountingChargeAddedDate.HasValue && !j.JobMetaNonWarranty.AccountingChargePaidDate.HasValue))
|
||||
Reasons.Add("Accounting Charge Required But Not Added Or Paid"); // Accounting Charge Required, but not added or paid
|
||||
else if (j.JobMetaNonWarranty.AccountingChargeRequiredDate.HasValue && !j.JobMetaNonWarranty.AccountingChargeAddedDate.HasValue)
|
||||
Reasons.Add("Accounting Charge Required But Not Added"); // Accounting Charge Required, but not added
|
||||
else if (j.JobMetaNonWarranty.AccountingChargeAddedDate.HasValue && !j.JobMetaNonWarranty.AccountingChargePaidDate.HasValue)
|
||||
Reasons.Add("Accounting Charge Added But Not Paid"); // Accounting Charge Added, but not paid
|
||||
else if (j.JobMetaNonWarranty.AccountingChargeRequiredDate.HasValue && !j.JobMetaNonWarranty.AccountingChargePaidDate.HasValue)
|
||||
Reasons.Add("Accounting Charge Required But Not Paid"); // Accounting Charge Required, but not paid
|
||||
|
||||
if (j.JobMetaNonWarranty.IsInsuranceClaim && !j.JobMetaInsurance.ClaimFormSentDate.HasValue)
|
||||
Reasons.Add("Insurance Claim Form Not Sent"); // Is Insurance Claim, but claim form not sent
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return (Reasons.Count > 0);
|
||||
}
|
||||
public static void OnCloseForced(this Job j, DiscoDataContext Database, User Technician, string Reason)
|
||||
{
|
||||
if (!j.CanCloseForced())
|
||||
throw new InvalidOperationException("Force Close was Denied");
|
||||
|
||||
// Write Log
|
||||
JobLog jobLog = new JobLog()
|
||||
{
|
||||
JobId = j.Id,
|
||||
TechUserId = Technician.UserId,
|
||||
Timestamp = DateTime.Now,
|
||||
Comments = string.Format("# Job Forcibly Closed\r\n{0}", string.IsNullOrWhiteSpace(Reason) ? "<no reason provided>" : Reason)
|
||||
};
|
||||
Database.JobLogs.Add(jobLog);
|
||||
|
||||
j.ClosedDate = DateTime.Now;
|
||||
j.ClosedTechUserId = Technician.UserId;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Reopen
|
||||
public static bool CanReopen(this Job j)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Job.Actions.Reopen))
|
||||
return false;
|
||||
|
||||
return j.ClosedDate.HasValue;
|
||||
}
|
||||
public static void OnReopen(this Job j)
|
||||
{
|
||||
if (!j.CanReopen())
|
||||
throw new InvalidOperationException("Reopen was Denied");
|
||||
|
||||
j.ClosedDate = null;
|
||||
j.ClosedTechUserId = null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Delete
|
||||
public static bool CanDelete(this Job j)
|
||||
{
|
||||
if (!UserService.CurrentAuthorization.Has(Claims.Job.Actions.Delete))
|
||||
return false;
|
||||
|
||||
return j.ClosedDate.HasValue;
|
||||
}
|
||||
public static void OnDelete(this Job j, DiscoDataContext Database)
|
||||
{
|
||||
// Job Sub Types
|
||||
j.JobSubTypes.Clear();
|
||||
|
||||
// Job Attachments
|
||||
foreach (var ja in j.JobAttachments.ToArray())
|
||||
ja.OnDelete(Database);
|
||||
j.JobAttachments.Clear();
|
||||
|
||||
// Job Components
|
||||
foreach (var jc in j.JobComponents.ToArray())
|
||||
Database.JobComponents.Remove(jc);
|
||||
j.JobComponents.Clear();
|
||||
|
||||
// Job Queue Jobs
|
||||
foreach (var jqj in j.JobQueues.ToArray())
|
||||
Database.JobQueueJobs.Remove(jqj);
|
||||
j.JobQueues.Clear();
|
||||
|
||||
// Job Logs
|
||||
foreach (var jl in j.JobLogs.ToArray())
|
||||
Database.JobLogs.Remove(jl);
|
||||
j.JobLogs.Clear();
|
||||
|
||||
// Job Meta
|
||||
if (j.JobMetaInsurance != null)
|
||||
{
|
||||
Database.JobMetaInsurances.Remove(j.JobMetaInsurance);
|
||||
j.JobMetaInsurance = null;
|
||||
}
|
||||
if (j.JobMetaNonWarranty != null)
|
||||
{
|
||||
Database.JobMetaNonWarranties.Remove(j.JobMetaNonWarranty);
|
||||
j.JobMetaNonWarranty = null;
|
||||
}
|
||||
if (j.JobMetaWarranty != null)
|
||||
{
|
||||
Database.JobMetaWarranties.Remove(j.JobMetaWarranty);
|
||||
j.JobMetaWarranty = null;
|
||||
}
|
||||
|
||||
// Job
|
||||
Database.Jobs.Remove(j);
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Documents;
|
||||
using Disco.Models.Services.Jobs.JobLists;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using Disco.Services.Plugins;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
@@ -195,5 +198,190 @@ namespace Disco.Services
|
||||
var statusId = j.CalculateStatusId();
|
||||
return new Tuple<string, string>(statusId, JobStatusDescription(statusId, j));
|
||||
}
|
||||
|
||||
public static List<DocumentTemplate> AvailableDocumentTemplates(this Job j, DiscoDataContext Database, User User, DateTime TimeStamp)
|
||||
{
|
||||
var dts = Database.DocumentTemplates.Include("JobSubTypes")
|
||||
.Where(dt => dt.Scope == DocumentTemplate.DocumentTemplateScopes.Job)
|
||||
.ToList();
|
||||
|
||||
foreach (var dt in dts.ToArray())
|
||||
{
|
||||
if (dt.JobSubTypes.Count != 0)
|
||||
{ // Filter Applied
|
||||
bool match = false;
|
||||
foreach (var st in j.JobSubTypes)
|
||||
{
|
||||
if (dt.JobSubTypes.Contains(st))
|
||||
{
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match)
|
||||
dts.Remove(dt);
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate Filters
|
||||
dts = dts.Where(dt => dt.FilterExpressionMatches(j, Database, User, TimeStamp, DocumentState.DefaultState())).ToList();
|
||||
|
||||
return dts;
|
||||
}
|
||||
|
||||
public static DateTime ValidateDateAfterOpened(this Job j, DateTime d)
|
||||
{
|
||||
if (d < j.OpenedDate)
|
||||
{
|
||||
if (d > j.OpenedDate.AddMinutes(-1))
|
||||
return j.OpenedDate;
|
||||
else
|
||||
throw new ArgumentException("The Date must be >= the Open Date.", "d");
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
public static string GenerateFaultDescription(this Job j, DiscoDataContext Database)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine("Faulty Components:");
|
||||
foreach (var jst in j.JobSubTypes)
|
||||
sb.Append("- ").AppendLine(jst.Description).AppendLine(" - ");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string GenerateFaultDescriptionFooter(this Job j, DiscoDataContext Database, PluginFeatureManifest WarrantyProviderDefinition)
|
||||
{
|
||||
var versionDisco = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
|
||||
return string.Format("Automation by Disco v{0}.{1}.{2:0000}.{3:0000} (Provider: {4} v{5})",
|
||||
versionDisco.Major, versionDisco.Minor, versionDisco.Build, versionDisco.Revision, WarrantyProviderDefinition.Id, WarrantyProviderDefinition.PluginManifest.Version.ToString(4));
|
||||
}
|
||||
|
||||
public static void UpdateSubTypes(this Job j, DiscoDataContext Database, List<JobSubType> SubTypes, bool AddComponents, User TechUser)
|
||||
{
|
||||
if (SubTypes == null || SubTypes.Count == 0)
|
||||
throw new ArgumentException("The Job must contain at least one Sub Type");
|
||||
|
||||
List<JobSubType> addedSubTypes = new List<JobSubType>();
|
||||
List<JobSubType> removedSubTypes = new List<JobSubType>();
|
||||
|
||||
// Removed Sub Types
|
||||
foreach (var t in j.JobSubTypes.ToArray())
|
||||
if (!SubTypes.Contains(t))
|
||||
{
|
||||
removedSubTypes.Add(t);
|
||||
j.JobSubTypes.Remove(t);
|
||||
}
|
||||
// Added Sub Types
|
||||
foreach (var t in SubTypes)
|
||||
if (!j.JobSubTypes.Contains(t))
|
||||
{
|
||||
addedSubTypes.Add(t);
|
||||
j.JobSubTypes.Add(t);
|
||||
}
|
||||
|
||||
// Write Log
|
||||
if (addedSubTypes.Count > 0 || removedSubTypes.Count > 0)
|
||||
{
|
||||
StringBuilder logBuilder = new StringBuilder();
|
||||
logBuilder.AppendLine("# Updated Job Sub Types");
|
||||
if (removedSubTypes.Count > 0)
|
||||
{
|
||||
logBuilder.AppendLine().AppendLine("Removed:");
|
||||
foreach (var t in removedSubTypes)
|
||||
logBuilder.Append("- **").Append(t.ToString()).AppendLine("**");
|
||||
}
|
||||
if (addedSubTypes.Count > 0)
|
||||
{
|
||||
logBuilder.AppendLine().AppendLine("Added:");
|
||||
foreach (var t in addedSubTypes)
|
||||
logBuilder.Append("- **").Append(t.ToString()).AppendLine("**");
|
||||
}
|
||||
Database.JobLogs.Add(new JobLog()
|
||||
{
|
||||
JobId = j.Id,
|
||||
TechUserId = TechUser.UserId,
|
||||
Timestamp = DateTime.Now,
|
||||
Comments = logBuilder.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
// Add Components
|
||||
if (AddComponents && addedSubTypes.Count > 0 && j.DeviceSerialNumber != null)
|
||||
{
|
||||
var components = Database.DeviceComponents.Include("JobSubTypes").Where(c => !c.DeviceModelId.HasValue || c.DeviceModelId == j.Device.DeviceModelId);
|
||||
var addedComponents = new List<DeviceComponent>();
|
||||
foreach (var c in components)
|
||||
{
|
||||
foreach (var st in c.JobSubTypes)
|
||||
{
|
||||
foreach (var jst in addedSubTypes)
|
||||
{
|
||||
if (st.JobTypeId == jst.JobTypeId && st.Id == jst.Id)
|
||||
{
|
||||
addedComponents.Add(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (addedComponents.Contains(c))
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach (var c in addedComponents)
|
||||
{
|
||||
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()
|
||||
{
|
||||
Job = j,
|
||||
TechUserId = TechUser.UserId,
|
||||
Cost = c.Cost,
|
||||
Description = c.Description
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<string> FilterCreatableTypePermissions(AuthorizationToken Authorization)
|
||||
{
|
||||
if (!Authorization.HasAll(Claims.Job.Types.CreateHMisc, Claims.Job.Types.CreateHNWar, Claims.Job.Types.CreateHWar, Claims.Job.Types.CreateSApp, Claims.Job.Types.CreateSImg, Claims.Job.Types.CreateSOS, Claims.Job.Types.CreateUMgmt))
|
||||
{
|
||||
// Must Filter
|
||||
List<string> allowedTypes = new List<string>(6);
|
||||
if (Authorization.Has(Claims.Job.Types.CreateHMisc))
|
||||
allowedTypes.Add(JobType.JobTypeIds.HMisc);
|
||||
if (Authorization.Has(Claims.Job.Types.CreateHNWar))
|
||||
allowedTypes.Add(JobType.JobTypeIds.HNWar);
|
||||
if (Authorization.Has(Claims.Job.Types.CreateHWar))
|
||||
allowedTypes.Add(JobType.JobTypeIds.HWar);
|
||||
if (Authorization.Has(Claims.Job.Types.CreateSApp))
|
||||
allowedTypes.Add(JobType.JobTypeIds.SApp);
|
||||
if (Authorization.Has(Claims.Job.Types.CreateSImg))
|
||||
allowedTypes.Add(JobType.JobTypeIds.SImg);
|
||||
if (Authorization.Has(Claims.Job.Types.CreateSOS))
|
||||
allowedTypes.Add(JobType.JobTypeIds.SOS);
|
||||
if (Authorization.Has(Claims.Job.Types.CreateUMgmt))
|
||||
allowedTypes.Add(JobType.JobTypeIds.UMgmt);
|
||||
|
||||
return allowedTypes;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IQueryable<JobType> FilterCreatableTypePermissions(this IQueryable<JobType> JobTypes, AuthorizationToken Authorization)
|
||||
{
|
||||
var allowedTypes = FilterCreatableTypePermissions(Authorization);
|
||||
|
||||
if (allowedTypes != null)
|
||||
{
|
||||
return JobTypes.Where(jt => allowedTypes.Contains(jt.Id));
|
||||
}
|
||||
|
||||
return JobTypes;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
using Disco.Models.Repository;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class JobFlagExtensions
|
||||
{
|
||||
|
||||
private static Dictionary<string, Dictionary<long, string>> allFlags;
|
||||
private static void CacheAllFlags()
|
||||
{
|
||||
if (allFlags == null)
|
||||
{
|
||||
var fType = typeof(Job.UserManagementFlags);
|
||||
var fMembers = fType.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
|
||||
|
||||
var flags = new Dictionary<string, Dictionary<long, string>>();
|
||||
foreach (var f in fMembers)
|
||||
{
|
||||
DisplayAttribute display = (DisplayAttribute)(f.GetCustomAttributes(typeof(DisplayAttribute), false)[0]);
|
||||
string gn = display.GroupName;
|
||||
Dictionary<long, string> g;
|
||||
if (!flags.TryGetValue(gn, out g))
|
||||
{
|
||||
g = new Dictionary<long, string>();
|
||||
flags.Add(gn, g);
|
||||
}
|
||||
g[(long)f.GetRawConstantValue()] = display.Name;
|
||||
}
|
||||
allFlags = flags;
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<string, List<Tuple<long, string, bool>>> ValidFlagsGrouped(this Job j)
|
||||
{
|
||||
Dictionary<string, List<Tuple<long, string, bool>>> validFlags = new Dictionary<string, List<Tuple<long, string, bool>>>();
|
||||
|
||||
CacheAllFlags();
|
||||
|
||||
var currentFlags = (long)(j.Flags ?? 0);
|
||||
|
||||
foreach (var jt in j.JobSubTypes)
|
||||
{
|
||||
Dictionary<long, string> g;
|
||||
if (allFlags.TryGetValue(jt.Id, out g))
|
||||
{
|
||||
validFlags[jt.Id] = g.Select(f => new Tuple<long, string, bool>(f.Key, f.Value, ((currentFlags & f.Key) == f.Key))).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
validFlags[jt.Id] = null;
|
||||
}
|
||||
}
|
||||
return validFlags;
|
||||
}
|
||||
public static Dictionary<long, Tuple<string, bool>> ValidFlags(this Job j)
|
||||
{
|
||||
Dictionary<long, Tuple<string, bool>> validFlags = new Dictionary<long, Tuple<string, bool>>();
|
||||
|
||||
CacheAllFlags();
|
||||
|
||||
var currentFlags = (long)(j.Flags ?? 0);
|
||||
|
||||
foreach (var jt in j.JobSubTypes)
|
||||
{
|
||||
Dictionary<long, string> g;
|
||||
if (allFlags.TryGetValue(jt.Id, out g))
|
||||
{
|
||||
foreach (var f in g)
|
||||
validFlags[f.Key] = new Tuple<string, bool>(string.Format("{0}: {1}", jt.Description, f.Value), ((currentFlags & f.Key) == f.Key));
|
||||
}
|
||||
}
|
||||
return validFlags;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Jobs.JobQueues;
|
||||
using Disco.Services.Users;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class JobQueueExtensions
|
||||
{
|
||||
|
||||
#region Edit Sla
|
||||
public static bool CanEditSla(this JobQueueJob jqj)
|
||||
{
|
||||
if (jqj.RemovedDate.HasValue)
|
||||
return false;
|
||||
|
||||
if (UserService.CurrentAuthorization.Has(Claims.Job.Properties.JobQueueProperties.EditAnySLA))
|
||||
{
|
||||
// Can edit ANY queue
|
||||
return true;
|
||||
}
|
||||
else if (UserService.CurrentAuthorization.Has(Claims.Job.Properties.JobQueueProperties.EditOwnSLA))
|
||||
{
|
||||
// Can edit from OWN queue
|
||||
return JobQueueService.UsersQueues(UserService.CurrentUser).Any(q => q.JobQueue.Id == jqj.JobQueueId);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static void OnEditSla(this JobQueueJob jqj, DateTime? SlaExpiresDate)
|
||||
{
|
||||
if (!jqj.CanEditSla())
|
||||
throw new InvalidOperationException("Editing job SLA for this queue is denied");
|
||||
|
||||
if (SlaExpiresDate.HasValue && jqj.AddedDate > SlaExpiresDate.Value)
|
||||
throw new ArgumentException("The SLA Expires Date must be greater than the Added Date", "SLAExpiresDate");
|
||||
|
||||
jqj.SLAExpiresDate = SlaExpiresDate;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Edit Priority
|
||||
public static bool CanEditPriority(this JobQueueJob jqj)
|
||||
{
|
||||
if (jqj.RemovedDate.HasValue)
|
||||
return false;
|
||||
|
||||
if (UserService.CurrentAuthorization.Has(Claims.Job.Properties.JobQueueProperties.EditAnyPriority))
|
||||
{
|
||||
// Can edit ANY queue
|
||||
return true;
|
||||
}
|
||||
else if (UserService.CurrentAuthorization.Has(Claims.Job.Properties.JobQueueProperties.EditOwnPriority))
|
||||
{
|
||||
// Can edit from OWN queue
|
||||
return JobQueueService.UsersQueues(UserService.CurrentUser).Any(q => q.JobQueue.Id == jqj.JobQueueId);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static void OnEditPriority(this JobQueueJob jqj, JobQueuePriority Priority)
|
||||
{
|
||||
if (!jqj.CanEditPriority())
|
||||
throw new InvalidOperationException("Editing job priority for this queue is denied");
|
||||
|
||||
jqj.Priority = Priority;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Edit Comments
|
||||
private static bool CanEditComments(this JobQueueJob jqj)
|
||||
{
|
||||
if (UserService.CurrentAuthorization.Has(Claims.Job.Properties.JobQueueProperties.EditAnyComments))
|
||||
{
|
||||
// Can edit ANY queue
|
||||
return true;
|
||||
}
|
||||
else if (UserService.CurrentAuthorization.Has(Claims.Job.Properties.JobQueueProperties.EditOwnComments))
|
||||
{
|
||||
// Can edit from OWN queue
|
||||
return JobQueueService.UsersQueues(UserService.CurrentUser).Any(q => q.JobQueue.Id == jqj.JobQueueId);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static bool CanEditAddedComment(this JobQueueJob jqj)
|
||||
{
|
||||
return jqj.CanEditComments();
|
||||
}
|
||||
public static bool CanEditRemovedComment(this JobQueueJob jqj)
|
||||
{
|
||||
if (!jqj.RemovedDate.HasValue)
|
||||
return false;
|
||||
|
||||
return jqj.CanEditComments();
|
||||
}
|
||||
public static void OnEditAddedComment(this JobQueueJob jqj, string AddedComment)
|
||||
{
|
||||
if (!jqj.CanEditAddedComment())
|
||||
throw new InvalidOperationException("Editing job added comments for this queue is denied");
|
||||
|
||||
jqj.AddedComment = string.IsNullOrWhiteSpace(AddedComment) ? null : AddedComment.Trim();
|
||||
}
|
||||
public static void OnEditRemovedComment(this JobQueueJob jqj, string RemovedComment)
|
||||
{
|
||||
if (!jqj.CanEditRemovedComment())
|
||||
throw new InvalidOperationException("Editing job removed comments for this queue is denied");
|
||||
|
||||
jqj.RemovedComment = string.IsNullOrWhiteSpace(RemovedComment) ? null : RemovedComment.Trim();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Remove
|
||||
public static bool CanRemove(this JobQueueJob jqj)
|
||||
{
|
||||
if (jqj.RemovedDate.HasValue)
|
||||
return false;
|
||||
|
||||
if (UserService.CurrentAuthorization.Has(Claims.Job.Actions.RemoveAnyQueues))
|
||||
{
|
||||
// Can remove from ANY queue
|
||||
return true;
|
||||
}
|
||||
else if (UserService.CurrentAuthorization.Has(Claims.Job.Actions.RemoveOwnQueues))
|
||||
{
|
||||
// Can remove from OWN queue
|
||||
return JobQueueService.UsersQueues(UserService.CurrentUser).Any(q => q.JobQueue.Id == jqj.JobQueueId);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static void OnRemove(this JobQueueJob jqj, User Technician, string Comment)
|
||||
{
|
||||
if (!jqj.CanRemove())
|
||||
throw new InvalidOperationException("Removing job from queue is denied");
|
||||
|
||||
jqj.RemovedDate = DateTime.Now;
|
||||
jqj.RemovedUserId = Technician.UserId;
|
||||
jqj.RemovedComment = string.IsNullOrWhiteSpace(Comment) ? null : Comment.Trim();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Add
|
||||
public static bool CanAddQueues(this Job j)
|
||||
{
|
||||
// Job Closed?
|
||||
if (j.ClosedDate.HasValue)
|
||||
return false;
|
||||
|
||||
if (UserService.CurrentAuthorization.HasAny(Claims.Job.Actions.AddAnyQueues, Claims.Job.Actions.AddOwnQueues))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
public static bool CanAddQueue(this Job j, JobQueue jq)
|
||||
{
|
||||
// Shortcut
|
||||
if (!j.CanAddQueues())
|
||||
return false;
|
||||
|
||||
// Already in Queue?
|
||||
if (j.JobQueues.Any(jjq => !jjq.RemovedDate.HasValue && jjq.JobQueueId == jq.Id))
|
||||
return false;
|
||||
|
||||
// Can add ANY queue
|
||||
if (UserService.CurrentAuthorization.Has(Claims.Job.Actions.AddAnyQueues))
|
||||
return true;
|
||||
|
||||
// Can add OWN queue
|
||||
if (UserService.CurrentAuthorization.Has(Claims.Job.Actions.AddOwnQueues))
|
||||
{
|
||||
return JobQueueService.UsersQueues(UserService.CurrentUser).Any(q => q.JobQueue.Id == jq.Id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public static JobQueueJob OnAddQueue(this Job j, DiscoDataContext Database, JobQueue jq, User Technician, string Comment, DateTime? SLAExpires, JobQueuePriority Priority)
|
||||
{
|
||||
if (!j.CanAddQueue(jq))
|
||||
throw new InvalidOperationException("Adding job to queue is denied");
|
||||
|
||||
if (SLAExpires.HasValue && SLAExpires.Value < DateTime.Now)
|
||||
throw new ArgumentException("The SLA Date must be greater than the current time", "SLAExpires");
|
||||
|
||||
var jqj = new JobQueueJob()
|
||||
{
|
||||
JobQueueId = jq.Id,
|
||||
JobId = j.Id,
|
||||
AddedDate = DateTime.Now,
|
||||
AddedUserId = Technician.UserId,
|
||||
AddedComment = string.IsNullOrWhiteSpace(Comment) ? null : Comment.Trim(),
|
||||
SLAExpiresDate = SLAExpires,
|
||||
Priority = Priority
|
||||
};
|
||||
|
||||
Database.JobQueueJobs.Add(jqj);
|
||||
return jqj;
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Disco.Services.Jobs
|
||||
{
|
||||
public static class Jobs
|
||||
{
|
||||
|
||||
public static Job Create(DiscoDataContext Database, Device device, User user, JobType type, List<JobSubType> subTypes, User initialTech, bool addAutoQueues = true)
|
||||
{
|
||||
Job j = new Job()
|
||||
{
|
||||
JobType = type,
|
||||
OpenedTechUserId = initialTech.UserId,
|
||||
OpenedTechUser = initialTech,
|
||||
OpenedDate = DateTime.Now
|
||||
};
|
||||
|
||||
// Device
|
||||
if (device != null)
|
||||
{
|
||||
j.Device = device;
|
||||
j.DeviceSerialNumber = device.SerialNumber;
|
||||
}
|
||||
|
||||
// User
|
||||
if (user != null)
|
||||
{
|
||||
j.User = user;
|
||||
j.UserId = user.UserId;
|
||||
}
|
||||
|
||||
// Sub Types
|
||||
List<JobSubType> jobSubTypes = subTypes.ToList();
|
||||
j.JobSubTypes = jobSubTypes;
|
||||
|
||||
Database.Jobs.Add(j);
|
||||
|
||||
// Job Queues
|
||||
if (addAutoQueues)
|
||||
{
|
||||
var queues = from st in subTypes
|
||||
from jq in st.JobQueues
|
||||
group st by jq into g
|
||||
select new { queue = g.Key, subTypes = g };
|
||||
foreach (var queue in queues)
|
||||
{
|
||||
var commentBuilder = new StringBuilder("Automatically added by:").AppendLine();
|
||||
foreach (var subType in queue.subTypes)
|
||||
{
|
||||
commentBuilder.AppendLine().Append("* ").Append(subType.Description);
|
||||
}
|
||||
|
||||
var jqj = new JobQueueJob()
|
||||
{
|
||||
JobQueueId = queue.queue.Id,
|
||||
Job = j,
|
||||
AddedDate = DateTime.Now,
|
||||
AddedUserId = initialTech.UserId,
|
||||
AddedComment = commentBuilder.ToString(),
|
||||
SLAExpiresDate = queue.queue.DefaultSLAExpiry.HasValue ? (DateTime?)DateTime.Now.AddMinutes(queue.queue.DefaultSLAExpiry.Value) : null,
|
||||
Priority = JobQueuePriority.Normal
|
||||
};
|
||||
|
||||
Database.JobQueueJobs.Add(jqj);
|
||||
}
|
||||
}
|
||||
|
||||
switch (type.Id)
|
||||
{
|
||||
case JobType.JobTypeIds.HWar:
|
||||
Database.JobMetaWarranties.Add(new JobMetaWarranty() { Job = j });
|
||||
break;
|
||||
case JobType.JobTypeIds.HNWar:
|
||||
Database.JobMetaNonWarranties.Add(new JobMetaNonWarranty() { Job = j });
|
||||
if (device != null)
|
||||
{
|
||||
// Add Job Components
|
||||
var components = Database.DeviceComponents.Include("JobSubTypes").Where(c => !c.DeviceModelId.HasValue || c.DeviceModelId == j.Device.DeviceModelId);
|
||||
var addedComponents = new List<DeviceComponent>();
|
||||
foreach (var c in components)
|
||||
{
|
||||
if (c.JobSubTypes.Count == 0)
|
||||
{ // No Filter
|
||||
addedComponents.Add(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var st in c.JobSubTypes)
|
||||
{
|
||||
foreach (var jst in jobSubTypes)
|
||||
{
|
||||
if (st.JobTypeId == jst.JobTypeId && st.Id == jst.Id)
|
||||
{
|
||||
addedComponents.Add(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (addedComponents.Contains(c))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var c in addedComponents)
|
||||
Database.JobComponents.Add(new JobComponent()
|
||||
{
|
||||
Job = j,
|
||||
TechUserId = initialTech.UserId,
|
||||
Cost = c.Cost,
|
||||
Description = c.Description
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Data.Repository.Monitor;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Job.Statistics;
|
||||
using Disco.Services.Tasks;
|
||||
using Quartz;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace Disco.Services.Jobs.Statistics
|
||||
{
|
||||
public class DailyOpenedClosed : ScheduledTask
|
||||
{
|
||||
|
||||
private static List<DailyOpenedClosedItem> _data;
|
||||
private static object _dataLock = new object();
|
||||
private static IDisposable _streamSubscription;
|
||||
|
||||
|
||||
public override string TaskName { get { return "Job Statistics - Daily Opened/Closed Task"; } }
|
||||
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)
|
||||
{
|
||||
// Trigger Daily @ 12:29am
|
||||
TriggerBuilder triggerBuilder = TriggerBuilder.Create().WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(0, 29));
|
||||
|
||||
this.ScheduleTask(triggerBuilder);
|
||||
}
|
||||
protected override void ExecuteTask()
|
||||
{
|
||||
using (var database = new DiscoDataContext())
|
||||
{
|
||||
UpdateDataHistory(database, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateDataHistory(DiscoDataContext Database, bool Refresh = false)
|
||||
{
|
||||
DateTime historyEnd = DateTime.Now.Date;
|
||||
|
||||
if (Refresh || _data == null || _data.Count == 0 || _data.Last().Timestamp < historyEnd)
|
||||
{
|
||||
lock (_dataLock)
|
||||
{
|
||||
if (Refresh || _data == null || _data.Count == 0 || _data.Last().Timestamp < historyEnd)
|
||||
{
|
||||
DateTime historyStart = DateTime.Now.AddDays(-28).Date;
|
||||
|
||||
// Initialize Memory Store
|
||||
List<DailyOpenedClosedItem> resultData;
|
||||
if (Refresh || _data == null)
|
||||
resultData = new List<DailyOpenedClosedItem>();
|
||||
else
|
||||
resultData = _data;
|
||||
|
||||
// Remove Old Data
|
||||
while (resultData.Count > 0 && resultData[0].Timestamp < historyStart)
|
||||
resultData.RemoveAt(0);
|
||||
|
||||
// Calculate Update Scope
|
||||
DateTime processDate = historyStart;
|
||||
if (resultData.Count > 0)
|
||||
processDate = resultData.Last().Timestamp.AddDays(-1);
|
||||
|
||||
// Cache Data
|
||||
while (processDate <= historyEnd)
|
||||
{
|
||||
resultData.Add(Data(Database, processDate));
|
||||
processDate = processDate.AddDays(1);
|
||||
}
|
||||
_data = resultData;
|
||||
|
||||
// Subscribe to Live Repository Events
|
||||
if (_streamSubscription != null)
|
||||
_streamSubscription.Dispose();
|
||||
_streamSubscription = RepositoryMonitor.StreamBeforeCommit.Where(
|
||||
e => e.EntityType == typeof(Job) &&
|
||||
(e.EventType == RepositoryMonitorEventType.Added || (e.EventType == RepositoryMonitorEventType.Modified && e.ModifiedProperties.Contains("ClosedDate")))).Subscribe(RepositoryEvent_JobChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RepositoryEvent_JobChange(RepositoryMonitorEvent e)
|
||||
{
|
||||
|
||||
if (e.EventType == RepositoryMonitorEventType.Added)
|
||||
{
|
||||
// New Job
|
||||
var todaysStats = _data.Last();
|
||||
todaysStats.OpenedJobs += 1;
|
||||
todaysStats.TotalJobs += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
DateTime? previousValue = e.GetPreviousPropertyValue<DateTime?>("ClosedDate");
|
||||
|
||||
if (previousValue.HasValue)
|
||||
{
|
||||
// Remove Statistics
|
||||
var statItem = _data.FirstOrDefault(i => i.Timestamp == previousValue.Value.Date);
|
||||
if (statItem != null)
|
||||
{
|
||||
statItem.ClosedJobs -= 1;
|
||||
statItem.TotalJobs += 1;
|
||||
}
|
||||
foreach (var affectedStat in _data.Where(i => i.Timestamp > previousValue))
|
||||
{
|
||||
affectedStat.TotalJobs += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DateTime? currentValue = e.GetCurrentPropertyValue<DateTime?>("ClosedDate");
|
||||
|
||||
if (currentValue.HasValue)
|
||||
{
|
||||
// Add Statistics
|
||||
// Remove Statistics
|
||||
var statItem = _data.FirstOrDefault(i => i.Timestamp == currentValue.Value.Date);
|
||||
if (statItem != null)
|
||||
{
|
||||
statItem.ClosedJobs += 1;
|
||||
statItem.TotalJobs -= 1;
|
||||
}
|
||||
foreach (var affectedStat in _data.Where(i => i.Timestamp > currentValue))
|
||||
{
|
||||
affectedStat.TotalJobs -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static DailyOpenedClosedItem Data(DiscoDataContext Database, DateTime ProcessDate)
|
||||
{
|
||||
DateTime processDateStart = ProcessDate;
|
||||
DateTime processDateEnd = ProcessDate.AddDays(1);
|
||||
|
||||
int totalJobs = Database.Jobs.Where(j => j.OpenedDate < processDateEnd && (!j.ClosedDate.HasValue || j.ClosedDate > processDateEnd)).Count();
|
||||
int openedJobs = Database.Jobs.Where(j => j.OpenedDate > processDateStart && j.OpenedDate < processDateEnd).Count();
|
||||
int closedJobs = Database.Jobs.Where(j => j.ClosedDate > processDateStart && j.ClosedDate < processDateEnd).Count();
|
||||
|
||||
return new DailyOpenedClosedItem()
|
||||
{
|
||||
Timestamp = ProcessDate,
|
||||
TotalJobs = totalJobs,
|
||||
OpenedJobs = openedJobs,
|
||||
ClosedJobs = closedJobs
|
||||
};
|
||||
}
|
||||
|
||||
public static List<DailyOpenedClosedItem> Data(DiscoDataContext Database, bool FilterUnimportantWeekends = false)
|
||||
{
|
||||
List<DailyOpenedClosedItem> resultData;
|
||||
|
||||
UpdateDataHistory(Database);
|
||||
|
||||
if (FilterUnimportantWeekends)
|
||||
resultData = _data.Where(i => (i.Timestamp.DayOfWeek != DayOfWeek.Saturday && i.Timestamp.DayOfWeek != DayOfWeek.Sunday) ||
|
||||
(i.OpenedJobs > 0 || i.ClosedJobs > 0)).ToList();
|
||||
else
|
||||
resultData = _data.ToList();
|
||||
|
||||
return resultData;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.ClientServices;
|
||||
using Disco.Models.Repository;
|
||||
|
||||
namespace Disco.Services.Plugins.Features.CertificateAuthorityProvider
|
||||
{
|
||||
[PluginFeatureCategory(DisplayName = "Certificate Authority Providers")]
|
||||
public abstract class CertificateAuthorityProviderFeature : PluginFeature
|
||||
{
|
||||
public abstract string CertificateProviderId { get; }
|
||||
|
||||
public abstract ProvisionAuthorityCertificatesResult ProvisionAuthorityCertificates(DiscoDataContext Database, Device Device, Enrol Enrolment);
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using Disco.Models.ClientServices;
|
||||
using Disco.Models.Repository;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Disco.Services.Plugins.Features.CertificateAuthorityProvider
|
||||
{
|
||||
public class ProvisionAuthorityCertificatesResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="Device"/> associated with the provision result.
|
||||
/// </summary>
|
||||
public Device Device { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Enrol"/> associated with the provision result.
|
||||
/// </summary>
|
||||
public Enrol Enrolment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of certificate thumbprints to be removed from the Trusted Root Certification Authorities store
|
||||
/// Matching certificates will be removed unless they match the <see cref="TrustedRootCertificates"/>.
|
||||
/// </summary>
|
||||
public List<string> TrustedRootCertificateRemoveThumbprints { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of certificates to be added to the Trusted Root Certification Authorities store on the client device.
|
||||
/// </summary>
|
||||
public List<byte[]> TrustedRootCertificates { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of certificate thumbprints to be removed from the Intermedate Certificate Authorities store.
|
||||
/// Matching certificates will be removed unless they match the <see cref="IntermediateCertificates"/>.
|
||||
/// </summary>
|
||||
public List<string> IntermediateCertificateRemoveThumbprints { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of certificates to be added to the Intermedate Certificate Authorities store on the client device.
|
||||
/// </summary>
|
||||
public List<byte[]> IntermediateCertificates { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.ClientServices;
|
||||
using Disco.Models.Repository;
|
||||
|
||||
namespace Disco.Services.Plugins.Features.CertificateProvider
|
||||
@@ -11,8 +7,8 @@ namespace Disco.Services.Plugins.Features.CertificateProvider
|
||||
[PluginFeatureCategory(DisplayName = "Certificate Providers")]
|
||||
public abstract class CertificateProviderFeature : PluginFeature
|
||||
{
|
||||
// Certificate Plugin Requirements
|
||||
public abstract string CertificateProviderId { get; }
|
||||
public abstract Tuple<DeviceCertificate, List<string>> AllocateCertificate(DiscoDataContext Database, Device Device);
|
||||
|
||||
public abstract ProvisionPersonalCertificateResult ProvisionPersonalCertificate(DiscoDataContext Database, Device Device, Enrol Enrolment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,129 +1,127 @@
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Logging;
|
||||
using Disco.Services.Logging.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Disco.Services.Plugins.Features.CertificateProvider
|
||||
{
|
||||
public class CertificateProvidersLog : LogBase
|
||||
{
|
||||
public enum EventTypeIds
|
||||
{
|
||||
RetrievalStarting = 10,
|
||||
RetrievalProgress,
|
||||
RetrievalFinished,
|
||||
RetrievalWarning = 15,
|
||||
RetrievalError,
|
||||
RetrievalCertificateStarting = 20,
|
||||
RetrievalCertificateFinished = 22,
|
||||
RetrievalCertificateWarning = 25,
|
||||
RetrievalCertificateError,
|
||||
Allocated = 40,
|
||||
AllocationFailed = 50,
|
||||
public class CertificateProvidersLog : LogBase
|
||||
{
|
||||
public enum EventTypeIds
|
||||
{
|
||||
RetrievalStarting = 10,
|
||||
RetrievalProgress,
|
||||
RetrievalFinished,
|
||||
RetrievalWarning = 15,
|
||||
RetrievalError,
|
||||
RetrievalCertificateStarting = 20,
|
||||
RetrievalCertificateFinished = 22,
|
||||
RetrievalCertificateWarning = 25,
|
||||
RetrievalCertificateError,
|
||||
Allocated = 40,
|
||||
AllocationFailed = 50,
|
||||
DisabledCertificate = 100,
|
||||
EnabledCertificate = 110,
|
||||
DeletedCertificate = 120,
|
||||
UpdatedCertificate = 130
|
||||
}
|
||||
private const int _ModuleId = 60;
|
||||
private static bool _IsCertificateRetrievalProcessing;
|
||||
private static string _CertificateRetrievalStatus;
|
||||
private static int _CertificateRetrievalProgress;
|
||||
public static CertificateProvidersLog Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return (CertificateProvidersLog)LogContext.LogModules[60];
|
||||
}
|
||||
}
|
||||
public static bool IsCertificateRetrievalProcessing
|
||||
{
|
||||
get
|
||||
{
|
||||
return CertificateProvidersLog._IsCertificateRetrievalProcessing;
|
||||
}
|
||||
}
|
||||
public override string ModuleDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Certificate Providers";
|
||||
}
|
||||
}
|
||||
public override int ModuleId
|
||||
{
|
||||
get
|
||||
{
|
||||
return 60;
|
||||
}
|
||||
}
|
||||
public override string ModuleName
|
||||
{
|
||||
get
|
||||
{
|
||||
return "CertificateProviders";
|
||||
}
|
||||
}
|
||||
[System.Diagnostics.DebuggerNonUserCode]
|
||||
public CertificateProvidersLog()
|
||||
{
|
||||
}
|
||||
private static void Log(CertificateProvidersLog.EventTypeIds EventTypeId, params object[] Args)
|
||||
{
|
||||
CertificateProvidersLog.Current.Log((int)EventTypeId, Args);
|
||||
}
|
||||
public static void LogRetrievalStarting(int CertificateCount, int CertificateIdFrom, int CertificateIdTo)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalStarting, new object[]
|
||||
{
|
||||
CertificateCount,
|
||||
CertificateIdFrom,
|
||||
CertificateIdTo
|
||||
});
|
||||
}
|
||||
public static void LogRetrievalFinished()
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalFinished, new object[0]);
|
||||
}
|
||||
public static void LogRetrievalWarning(string Message)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalWarning, new object[]
|
||||
{
|
||||
Message
|
||||
});
|
||||
}
|
||||
public static void LogRetrievalError(string Message)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalError, new object[]
|
||||
{
|
||||
Message
|
||||
});
|
||||
}
|
||||
public static void LogRetrievalCertificateStarting(string CertificateId)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalCertificateStarting, CertificateId);
|
||||
}
|
||||
public static void LogRetrievalCertificateFinished(string CertificateId)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalCertificateFinished, CertificateId);
|
||||
}
|
||||
public static void LogRetrievalCertificateWarning(string CertificateId, string Message)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalCertificateWarning, CertificateId, Message);
|
||||
}
|
||||
public static void LogRetrievalCertificateError(string CertificateId, string Message)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalCertificateError, CertificateId, Message);
|
||||
}
|
||||
public static void LogAllocated(string CertificateId, string DeviceSerialNumber)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.Allocated, CertificateId, DeviceSerialNumber);
|
||||
}
|
||||
public static void LogAllocationFailed(string DeviceSerialNumber)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.AllocationFailed, DeviceSerialNumber);
|
||||
}
|
||||
}
|
||||
private const int _ModuleId = 60;
|
||||
private static bool _IsCertificateRetrievalProcessing;
|
||||
private static string _CertificateRetrievalStatus;
|
||||
private static int _CertificateRetrievalProgress;
|
||||
public static CertificateProvidersLog Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return (CertificateProvidersLog)LogContext.LogModules[60];
|
||||
}
|
||||
}
|
||||
public static bool IsCertificateRetrievalProcessing
|
||||
{
|
||||
get
|
||||
{
|
||||
return CertificateProvidersLog._IsCertificateRetrievalProcessing;
|
||||
}
|
||||
}
|
||||
public override string ModuleDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Certificate Providers";
|
||||
}
|
||||
}
|
||||
public override int ModuleId
|
||||
{
|
||||
get
|
||||
{
|
||||
return 60;
|
||||
}
|
||||
}
|
||||
public override string ModuleName
|
||||
{
|
||||
get
|
||||
{
|
||||
return "CertificateProviders";
|
||||
}
|
||||
}
|
||||
[System.Diagnostics.DebuggerNonUserCode]
|
||||
public CertificateProvidersLog()
|
||||
{
|
||||
}
|
||||
private static void Log(CertificateProvidersLog.EventTypeIds EventTypeId, params object[] Args)
|
||||
{
|
||||
CertificateProvidersLog.Current.Log((int)EventTypeId, Args);
|
||||
}
|
||||
public static void LogRetrievalStarting(int CertificateCount, int CertificateIdFrom, int CertificateIdTo)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalStarting, new object[]
|
||||
{
|
||||
CertificateCount,
|
||||
CertificateIdFrom,
|
||||
CertificateIdTo
|
||||
});
|
||||
}
|
||||
public static void LogRetrievalFinished()
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalFinished, new object[0]);
|
||||
}
|
||||
public static void LogRetrievalWarning(string Message)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalWarning, new object[]
|
||||
{
|
||||
Message
|
||||
});
|
||||
}
|
||||
public static void LogRetrievalError(string Message)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalError, new object[]
|
||||
{
|
||||
Message
|
||||
});
|
||||
}
|
||||
public static void LogRetrievalCertificateStarting(string CertificateId)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalCertificateStarting, CertificateId);
|
||||
}
|
||||
public static void LogRetrievalCertificateFinished(string CertificateId)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalCertificateFinished, CertificateId);
|
||||
}
|
||||
public static void LogRetrievalCertificateWarning(string CertificateId, string Message)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalCertificateWarning, CertificateId, Message);
|
||||
}
|
||||
public static void LogRetrievalCertificateError(string CertificateId, string Message)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalCertificateError, CertificateId, Message);
|
||||
}
|
||||
public static void LogAllocated(string CertificateId, string DeviceSerialNumber)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.Allocated, CertificateId, DeviceSerialNumber);
|
||||
}
|
||||
public static void LogAllocationFailed(string DeviceSerialNumber)
|
||||
{
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.AllocationFailed, DeviceSerialNumber);
|
||||
}
|
||||
public static void LogDisabledCertificate(DeviceCertificate Certificate, string Reason)
|
||||
{
|
||||
CertificateProvidersLog.Log(EventTypeIds.DisabledCertificate, Certificate.Name, Certificate.Id, Reason);
|
||||
@@ -140,210 +138,210 @@ namespace Disco.Services.Plugins.Features.CertificateProvider
|
||||
{
|
||||
CertificateProvidersLog.Log(EventTypeIds.UpdatedCertificate, Certificate.Name, Certificate.Id, Reason);
|
||||
}
|
||||
|
||||
public static void LogCertificateRetrievalProgress(bool? IsProcessing, int? Progress, string Status)
|
||||
{
|
||||
bool flag = IsProcessing.HasValue;
|
||||
if (flag)
|
||||
{
|
||||
CertificateProvidersLog._IsCertificateRetrievalProcessing = IsProcessing.Value;
|
||||
}
|
||||
flag = CertificateProvidersLog._IsCertificateRetrievalProcessing;
|
||||
if (flag)
|
||||
{
|
||||
bool flag2 = Status != null;
|
||||
if (flag2)
|
||||
{
|
||||
CertificateProvidersLog._CertificateRetrievalStatus = Status;
|
||||
}
|
||||
flag2 = Progress.HasValue;
|
||||
if (flag2)
|
||||
{
|
||||
CertificateProvidersLog._CertificateRetrievalProgress = Progress.Value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CertificateProvidersLog._CertificateRetrievalStatus = null;
|
||||
CertificateProvidersLog._CertificateRetrievalProgress = 0;
|
||||
}
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalProgress, new object[]
|
||||
{
|
||||
CertificateProvidersLog._IsCertificateRetrievalProcessing,
|
||||
CertificateProvidersLog._CertificateRetrievalProgress,
|
||||
CertificateProvidersLog._CertificateRetrievalStatus
|
||||
});
|
||||
}
|
||||
protected override System.Collections.Generic.List<LogEventType> LoadEventTypes()
|
||||
{
|
||||
return new System.Collections.Generic.List<LogEventType>
|
||||
{
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalStarting,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Starting",
|
||||
Format = "Starting retrieval of {0} certificate/s ({1} to {2})",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalProgress,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Progress",
|
||||
Format = "Processing: {0}; {1}% Complete; Status: {2}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = false,
|
||||
UseDisplay = false
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalFinished,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Finished",
|
||||
Format = "Retrieval of Certificates Complete",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalWarning,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Warning",
|
||||
Format = "Retrieval Warning: {0}",
|
||||
Severity = 1,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalError,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Error",
|
||||
Format = "Retrieval Error: {0}",
|
||||
Severity = 2,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalCertificateStarting,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Certificate Starting",
|
||||
Format = "Retrieving Certificate: {0}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalCertificateFinished,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Certificate Finished",
|
||||
Format = "Certificate Retrieved: {0}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalCertificateWarning,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Certificate Warning",
|
||||
Format = "{0} Certificate Warning: {1}",
|
||||
Severity = 1,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalCertificateError,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Certificate Error",
|
||||
Format = "{0} Certificate Error: {1}",
|
||||
Severity = 2,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.Allocated,
|
||||
ModuleId = 60,
|
||||
Name = "Allocated",
|
||||
Format = "Certificate {0} allocated to {1}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.AllocationFailed,
|
||||
ModuleId = 60,
|
||||
Name = "Allocation Failed",
|
||||
Format = "No certificates available for Device: {0}",
|
||||
Severity = 2,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.DisabledCertificate,
|
||||
ModuleId = 60,
|
||||
Name = "Disabled Certificate",
|
||||
Format = "Certificate Disabled: {0} [{1}], Reason: {2}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
|
||||
public static void LogCertificateRetrievalProgress(bool? IsProcessing, int? Progress, string Status)
|
||||
{
|
||||
bool flag = IsProcessing.HasValue;
|
||||
if (flag)
|
||||
{
|
||||
CertificateProvidersLog._IsCertificateRetrievalProcessing = IsProcessing.Value;
|
||||
}
|
||||
flag = CertificateProvidersLog._IsCertificateRetrievalProcessing;
|
||||
if (flag)
|
||||
{
|
||||
bool flag2 = Status != null;
|
||||
if (flag2)
|
||||
{
|
||||
CertificateProvidersLog._CertificateRetrievalStatus = Status;
|
||||
}
|
||||
flag2 = Progress.HasValue;
|
||||
if (flag2)
|
||||
{
|
||||
CertificateProvidersLog._CertificateRetrievalProgress = Progress.Value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CertificateProvidersLog._CertificateRetrievalStatus = null;
|
||||
CertificateProvidersLog._CertificateRetrievalProgress = 0;
|
||||
}
|
||||
CertificateProvidersLog.Log(CertificateProvidersLog.EventTypeIds.RetrievalProgress, new object[]
|
||||
{
|
||||
CertificateProvidersLog._IsCertificateRetrievalProcessing,
|
||||
CertificateProvidersLog._CertificateRetrievalProgress,
|
||||
CertificateProvidersLog._CertificateRetrievalStatus
|
||||
});
|
||||
}
|
||||
protected override System.Collections.Generic.List<LogEventType> LoadEventTypes()
|
||||
{
|
||||
return new System.Collections.Generic.List<LogEventType>
|
||||
{
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.EnabledCertificate,
|
||||
ModuleId = 60,
|
||||
Name = "Enabled Certificate",
|
||||
Format = "Certificate Disabled: {0} [{1}], Reason: {2}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.DeletedCertificate,
|
||||
ModuleId = 60,
|
||||
Name = "Deleted Certificate",
|
||||
Format = "Certificate Deleted: {0} [{1}], Reason: {2}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.UpdatedCertificate,
|
||||
ModuleId = 60,
|
||||
Name = "Updated Certificate",
|
||||
Format = "Certificate Updated: {0} [{1}], Reason: {2}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalStarting,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Starting",
|
||||
Format = "Starting retrieval of {0} certificate/s ({1} to {2})",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalProgress,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Progress",
|
||||
Format = "Processing: {0}; {1}% Complete; Status: {2}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = false,
|
||||
UseDisplay = false
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalFinished,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Finished",
|
||||
Format = "Retrieval of Certificates Complete",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalWarning,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Warning",
|
||||
Format = "Retrieval Warning: {0}",
|
||||
Severity = 1,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalError,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Error",
|
||||
Format = "Retrieval Error: {0}",
|
||||
Severity = 2,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalCertificateStarting,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Certificate Starting",
|
||||
Format = "Retrieving Certificate: {0}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalCertificateFinished,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Certificate Finished",
|
||||
Format = "Certificate Retrieved: {0}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalCertificateWarning,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Certificate Warning",
|
||||
Format = "{0} Certificate Warning: {1}",
|
||||
Severity = 1,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.RetrievalCertificateError,
|
||||
ModuleId = 60,
|
||||
Name = "Retrieval Certificate Error",
|
||||
Format = "{0} Certificate Error: {1}",
|
||||
Severity = 2,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.Allocated,
|
||||
ModuleId = 60,
|
||||
Name = "Allocated",
|
||||
Format = "Certificate {0} allocated to {1}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.AllocationFailed,
|
||||
ModuleId = 60,
|
||||
Name = "Allocation Failed",
|
||||
Format = "No certificates available for Device: {0}",
|
||||
Severity = 2,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.DisabledCertificate,
|
||||
ModuleId = 60,
|
||||
Name = "Disabled Certificate",
|
||||
Format = "Certificate Disabled: {0} [{1}], Reason: {2}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.EnabledCertificate,
|
||||
ModuleId = 60,
|
||||
Name = "Enabled Certificate",
|
||||
Format = "Certificate Disabled: {0} [{1}], Reason: {2}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.DeletedCertificate,
|
||||
ModuleId = 60,
|
||||
Name = "Deleted Certificate",
|
||||
Format = "Certificate Deleted: {0} [{1}], Reason: {2}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.UpdatedCertificate,
|
||||
ModuleId = 60,
|
||||
Name = "Updated Certificate",
|
||||
Format = "Certificate Updated: {0} [{1}], Reason: {2}",
|
||||
Severity = 0,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
using Disco.Models.ClientServices;
|
||||
using Disco.Models.Repository;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Disco.Services.Plugins.Features.CertificateProvider
|
||||
{
|
||||
public class ProvisionPersonalCertificateResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="Device"/> associated with the provision result.
|
||||
/// </summary>
|
||||
public Device Device { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Enrol"/> associated with the provision result.
|
||||
/// </summary>
|
||||
public Enrol Enrolment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of <see cref="DeviceCertificate"/> assigned to the device.
|
||||
/// </summary>
|
||||
public List<DeviceCertificate> AllocatedCertificates { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of certificate thumbprints to be removed from the Personal Certificate store.
|
||||
/// Matching certificates will be removed unless they match the <see cref="AllocatedCertificate"/>.
|
||||
/// </summary>
|
||||
public List<string> RemoveCertificateThumbprints { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
using Disco.Models.ClientServices;
|
||||
using Disco.Models.Repository;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Disco.Services.Plugins.Features.WirelessProfileProvider
|
||||
{
|
||||
public class ProvisionWirelessProfilesResult
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Device"/> associated with the provision result.
|
||||
/// </summary>
|
||||
public Device Device { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Enrol"/> associated with the provision result.
|
||||
/// </summary>
|
||||
public Enrol Enrolment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of wireless profiles to add to the client device.
|
||||
/// If the wireless profile already exists it will be ignored.
|
||||
/// </summary>
|
||||
public List<WirelessProfile> Profiles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of transformations to be applied to existing XML wireless profiles found on the client device.
|
||||
/// </summary>
|
||||
public List<WirelessProfileTransformation> Transformations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of wireless profile names to be removed from the client device.
|
||||
/// </summary>
|
||||
public List<string> RemoveNames { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
namespace Disco.Services.Plugins.Features.WirelessProfileProvider
|
||||
{
|
||||
public class WirelessProfile
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The name of the wireless profile, typically the SSID
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The guid of the associated interface. Null to deploy to all supported interfaces.
|
||||
/// </summary>
|
||||
public Guid? InterfaceGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the profile should be overwritten even if it already exists
|
||||
/// </summary>
|
||||
public bool ForceDeployment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The wireless profile XML definition
|
||||
/// </summary>
|
||||
public string ProfileXml { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.ClientServices;
|
||||
using Disco.Models.Repository;
|
||||
|
||||
namespace Disco.Services.Plugins.Features.WirelessProfileProvider
|
||||
{
|
||||
[PluginFeatureCategory(DisplayName = "Wireless Profile Providers")]
|
||||
public abstract class WirelessProfileProviderFeature : PluginFeature
|
||||
{
|
||||
|
||||
public abstract string WirelessProfileProviderId { get; }
|
||||
|
||||
public abstract ProvisionWirelessProfilesResult ProvisionWirelessProfiles(DiscoDataContext Database, Device Device, Enrol Enrolment);
|
||||
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
namespace Disco.Services.Plugins.Features.WirelessProfileProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// A transform to be applied to the matching <see cref="Name"/>.
|
||||
/// </summary>
|
||||
public class WirelessProfileTransformation
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The name of the wireless profile related to this transformation, typically the SSID
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The regular expression to evaluate against the wireless profile XML
|
||||
/// </summary>
|
||||
public string RegularExpression { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The replacement string used when evaluating the regular expression
|
||||
/// </summary>
|
||||
public string RegularExpressionReplacement { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -134,10 +134,8 @@ namespace Disco.Services.Plugins
|
||||
{
|
||||
return new List<string>()
|
||||
{
|
||||
"BitMiracle.LibTiff.NET",
|
||||
"C5",
|
||||
"Common.Logging",
|
||||
"DiffieHellman",
|
||||
"Disco.BI",
|
||||
"Disco.Data",
|
||||
"Disco.Models",
|
||||
@@ -154,14 +152,17 @@ namespace Disco.Services.Plugins
|
||||
"Microsoft.AspNet.SignalR.Core",
|
||||
"Microsoft.AspNet.SignalR.SystemWeb",
|
||||
"Microsoft.Owin",
|
||||
"Microsoft.Owin.Security",
|
||||
"Microsoft.Owin.Host.SystemWeb",
|
||||
"Microsoft.Owin.Security",
|
||||
"Microsoft.Web.Infrastructure",
|
||||
"Newtonsoft.Json",
|
||||
"Org.Mentalis.Security",
|
||||
"Owin",
|
||||
"PdfiumViewer",
|
||||
"PdfSharp",
|
||||
"PList",
|
||||
"Quartz",
|
||||
"RazorGenerator.Mvc",
|
||||
"Renci.SshNet",
|
||||
"Spring.Core",
|
||||
"System.Data.SqlServerCe",
|
||||
"System.Data.SqlServerCe.Entity",
|
||||
@@ -181,7 +182,6 @@ namespace Disco.Services.Plugins
|
||||
"System.Web.WebPages",
|
||||
"System.Web.WebPages.Razor",
|
||||
"T4MVCExtensions",
|
||||
"Tamir.SharpSSH",
|
||||
"WebActivatorEx",
|
||||
"zxing"
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Services.Interop.DiscoServices;
|
||||
using Disco.Services.Interop.DiscoServices;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -187,15 +186,22 @@ namespace Disco.Services.Plugins
|
||||
}
|
||||
public static bool TryGetPluginFeature(string PluginFeatureId, Type CategoryType, out PluginFeatureManifest PluginFeatureManifest)
|
||||
{
|
||||
PluginFeatureManifest = null;
|
||||
|
||||
if (_PluginManifests == null)
|
||||
{
|
||||
PluginFeatureManifest = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var featureManifest = _PluginManifests.Values.SelectMany(pm => pm.Features).Where(fm => fm.Id == PluginFeatureId).FirstOrDefault();
|
||||
var featureManifest = _PluginManifests.Values
|
||||
.SelectMany(pm => pm.Features)
|
||||
.Where(fm => fm.Id == PluginFeatureId)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (featureManifest == null)
|
||||
{
|
||||
PluginFeatureManifest = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CategoryType == null)
|
||||
{
|
||||
@@ -210,7 +216,10 @@ namespace Disco.Services.Plugins
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
PluginFeatureManifest = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
public static PluginFeatureManifest GetPluginFeature(string PluginFeatureId)
|
||||
|
||||
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("2.1.0.0")]
|
||||
[assembly: AssemblyFileVersion("2.1.0.0")]
|
||||
[assembly: AssemblyVersion("2.2.16272.1003")]
|
||||
[assembly: AssemblyFileVersion("2.2.16272.1003")]
|
||||
@@ -37,5 +37,8 @@ namespace Disco.Services.Tasks
|
||||
void Finished(string FinishedMessage, string FinishedUrl);
|
||||
|
||||
void SetTaskException(Exception TaskException);
|
||||
|
||||
void LogWarning(string Message);
|
||||
void LogInformation(string Message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.Services.Tasks
|
||||
{
|
||||
public class ScheduledTaskMockStatus : IScheduledTaskStatus
|
||||
{
|
||||
public string TaskName { get; private set; }
|
||||
|
||||
public byte Progress { get; set; }
|
||||
public string CurrentProcess { get; set; }
|
||||
public string CurrentDescription { get; set; }
|
||||
@@ -22,6 +20,11 @@ namespace Disco.Services.Tasks
|
||||
|
||||
public Exception TaskException { get; set; }
|
||||
|
||||
public ScheduledTaskMockStatus(string TaskName)
|
||||
{
|
||||
this.TaskName = TaskName;
|
||||
}
|
||||
|
||||
private byte CalculateProgressValue(byte Progress)
|
||||
{
|
||||
return (byte)((Progress * this.ProgressMultiplier) + this.ProgressOffset);
|
||||
@@ -90,11 +93,25 @@ namespace Disco.Services.Tasks
|
||||
this.TaskException = TaskException;
|
||||
}
|
||||
|
||||
public void LogWarning(string Message)
|
||||
{
|
||||
ScheduledTasksLog.LogScheduledTaskWarning(TaskName, null, Message);
|
||||
}
|
||||
|
||||
public void LogInformation(string Message)
|
||||
{
|
||||
ScheduledTasksLog.LogScheduledTaskInformation(TaskName, null, Message);
|
||||
}
|
||||
|
||||
[Obsolete("Use the constructor which requires a TaskName instead")]
|
||||
public static ScheduledTaskMockStatus Create()
|
||||
{
|
||||
return new ScheduledTaskMockStatus();
|
||||
return new ScheduledTaskMockStatus("Unknown Task");
|
||||
}
|
||||
|
||||
public static ScheduledTaskMockStatus Create(string TaskName)
|
||||
{
|
||||
return new ScheduledTaskMockStatus(TaskName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,16 @@ namespace Disco.Services.Tasks
|
||||
this._progress = 0;
|
||||
}
|
||||
|
||||
public void LogWarning(string Message)
|
||||
{
|
||||
ScheduledTasksLog.LogScheduledTaskWarning(TaskName, SessionId, Message);
|
||||
}
|
||||
|
||||
public void LogInformation(string Message)
|
||||
{
|
||||
ScheduledTasksLog.LogScheduledTaskInformation(TaskName, SessionId, Message);
|
||||
}
|
||||
|
||||
#region Progress Actions
|
||||
private byte CalculateProgressValue(byte Progress)
|
||||
{
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace Disco.Services.Tasks
|
||||
InitializeScheduledTasksExceptionWithInner,
|
||||
ScheduledTasksException = 30,
|
||||
ScheduledTasksExceptionWithInner,
|
||||
ScheduledTasksWarning,
|
||||
ScheduledTasksInformation,
|
||||
ScheduledTaskExecuted = 50,
|
||||
ScheduledTaskFinished = 80,
|
||||
}
|
||||
@@ -98,6 +100,16 @@ namespace Disco.Services.Tasks
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogScheduledTaskInformation(string ScheduledTaskName, string SessionId, string Message)
|
||||
{
|
||||
Log(EventTypeIds.ScheduledTasksInformation, ScheduledTaskName, SessionId, Message);
|
||||
}
|
||||
|
||||
public static void LogScheduledTaskWarning(string ScheduledTaskName, string SessionId, string Message)
|
||||
{
|
||||
Log(EventTypeIds.ScheduledTasksWarning, ScheduledTaskName, SessionId, Message);
|
||||
}
|
||||
|
||||
protected override List<Logging.Models.LogEventType> LoadEventTypes()
|
||||
{
|
||||
return new System.Collections.Generic.List<LogEventType>
|
||||
@@ -178,7 +190,29 @@ namespace Disco.Services.Tasks
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ScheduledTasksWarning,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Scheduled Task Warning",
|
||||
Format = "Scheduled Task '{0}' Warning: {2}; Session Id: {1}",
|
||||
Severity = (int)LogEventType.Severities.Warning,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ScheduledTasksInformation,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Scheduled Task Information",
|
||||
Format = "Scheduled Task '{0}' Information: {2}; Session Id: {1}",
|
||||
Severity = (int)LogEventType.Severities.Information,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ScheduledTaskExecuted,
|
||||
@@ -201,7 +235,7 @@ namespace Disco.Services.Tasks
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Documents;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
@@ -20,5 +24,29 @@ namespace Disco.Services
|
||||
{
|
||||
return ActiveDirectory.FriendlyAccountId(u.UserId);
|
||||
}
|
||||
|
||||
public static List<DocumentTemplate> AvailableDocumentTemplates(this User u, DiscoDataContext Database, User User, DateTime TimeStamp)
|
||||
{
|
||||
var dts = Database.DocumentTemplates.Include("JobSubTypes")
|
||||
.Where(dt => dt.Scope == DocumentTemplate.DocumentTemplateScopes.User)
|
||||
.ToArray()
|
||||
.Where(dt => dt.FilterExpressionMatches(u, Database, User, TimeStamp, DocumentState.DefaultState())).ToList();
|
||||
|
||||
return dts;
|
||||
}
|
||||
|
||||
public static List<DeviceUserAssignment> CurrentDeviceUserAssignments(this User u)
|
||||
{
|
||||
return u.DeviceUserAssignments.Where(dua => !dua.UnassignedDate.HasValue).ToList();
|
||||
}
|
||||
|
||||
public static bool CanCreateJob(this User u)
|
||||
{
|
||||
if (!JobActionExtensions.CanCreate())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Users;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class UserFlagExtensions
|
||||
{
|
||||
|
||||
#region Edit Comments
|
||||
public static bool CanEditComments(this UserFlagAssignment fa)
|
||||
{
|
||||
return UserService.CurrentAuthorization.Has(Claims.User.Actions.EditFlags);
|
||||
}
|
||||
public static void OnEditComments(this UserFlagAssignment fa, string Comments)
|
||||
{
|
||||
if (!fa.CanEditComments())
|
||||
throw new InvalidOperationException("Editing comments for user flags is denied");
|
||||
|
||||
fa.Comments = string.IsNullOrWhiteSpace(Comments) ? null : Comments.Trim();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Remove
|
||||
public static bool CanRemove(this UserFlagAssignment fa)
|
||||
{
|
||||
if (fa.RemovedDate.HasValue)
|
||||
return false;
|
||||
|
||||
return UserService.CurrentAuthorization.Has(Claims.User.Actions.RemoveFlags);
|
||||
}
|
||||
public static void OnRemove(this UserFlagAssignment fa, User Technician)
|
||||
{
|
||||
if (!fa.CanRemove())
|
||||
throw new InvalidOperationException("Removing user flags is denied");
|
||||
|
||||
fa.RemovedDate = DateTime.Now;
|
||||
fa.RemovedUserId = Technician.UserId;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Add
|
||||
public static bool CanAddUserFlags(this User u)
|
||||
{
|
||||
return UserService.CurrentAuthorization.Has(Claims.User.Actions.AddFlags);
|
||||
}
|
||||
public static bool CanAddUserFlag(this User u, UserFlag flag)
|
||||
{
|
||||
// Shortcut
|
||||
if (!u.CanAddUserFlags())
|
||||
return false;
|
||||
|
||||
// Already has User Flag?
|
||||
if (u.UserFlagAssignments.Any(fa => !fa.RemovedDate.HasValue && fa.UserFlagId == flag.Id))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
public static UserFlagAssignment OnAddUserFlag(this User u, DiscoDataContext Database, UserFlag flag, User Technician, string Comments)
|
||||
{
|
||||
if (!u.CanAddUserFlag(flag))
|
||||
throw new InvalidOperationException("Adding user flag is denied");
|
||||
|
||||
var fa = new UserFlagAssignment()
|
||||
{
|
||||
UserFlagId = flag.Id,
|
||||
UserId = u.UserId,
|
||||
AddedDate = DateTime.Now,
|
||||
AddedUserId = Technician.UserId,
|
||||
Comments = string.IsNullOrWhiteSpace(Comments) ? null : Comments.Trim()
|
||||
};
|
||||
|
||||
Database.UserFlagAssignments.Add(fa);
|
||||
return fa;
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
<package id="Owin" version="1.0" targetFramework="net45" />
|
||||
<package id="PdfiumViewer" version="2.10.0.0" targetFramework="net45" />
|
||||
<package id="PDFsharp" version="1.50.4000-beta3b" targetFramework="net45" />
|
||||
<package id="plist.net" version="1.0" targetFramework="net45" />
|
||||
<package id="RazorGenerator.Mvc" version="2.2.3" targetFramework="net45" />
|
||||
<package id="Rx-Core" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Rx-Interfaces" version="2.2.5" targetFramework="net45" />
|
||||
|
||||
Reference in New Issue
Block a user