diff --git a/Disco.Services/Authorization/AccessDeniedException.cs b/Disco.Services/Authorization/AccessDeniedException.cs index 05a97a1e..7507bd50 100644 --- a/Disco.Services/Authorization/AccessDeniedException.cs +++ b/Disco.Services/Authorization/AccessDeniedException.cs @@ -8,26 +8,36 @@ namespace Disco.Services.Authorization { public class AccessDeniedException : Exception { - private string _message { get; set; } + private string message { get; set; } + private string resource { get; set; } - public AccessDeniedException(string Message) + public AccessDeniedException(string Message, string Resource) { - this._message = Message; + this.message = Message; + this.resource = Resource; } public override string Message { get { - if (this._message == null) + if (this.message == null) { return "Your account does not have the required permission to access this Disco feature."; } else { - return this._message; + return this.message; } } } + + public string Resource + { + get + { + return this.resource; + } + } } } diff --git a/Disco.Services/Authorization/AuthorizationLog.cs b/Disco.Services/Authorization/AuthorizationLog.cs new file mode 100644 index 00000000..e9400fd0 --- /dev/null +++ b/Disco.Services/Authorization/AuthorizationLog.cs @@ -0,0 +1,152 @@ +using Disco.Models.Repository; +using Disco.Services.Logging; +using Disco.Services.Logging.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Disco.Services.Authorization +{ + public class AuthorizationLog : LogBase + { + private const int moduleId = 100; + private const string moduleName = "Authorization"; + private const string moduleDescription = "Authorization Log"; + + public override int ModuleId { get { return moduleId; } } + public override string ModuleName { get { return moduleName; } } + public override string ModuleDescription { get { return moduleDescription; } } + + public enum EventTypeIds : int + { + AccessDenied = 5, + RoleCreated = 100, + RoleDeleted = 110, + RoleConfiguredRenamed = 151, + RoleConfiguredSubjectsAdded = 152, + RoleConfiguredSubjectsRemoved = 153, + RoleConfiguredClaimsAdded = 154, + RoleConfiguredClaimsRemoved = 155, + } + + public static AuthorizationLog Current { get { return (AuthorizationLog)LogContext.LogModules[moduleId]; } } + + private static void Log(EventTypeIds EventTypeId, params object[] Args) + { + Current.Log((int)EventTypeId, Args); + } + + protected override List LoadEventTypes() + { + List eventTypes = new List() { + new LogEventType() { + Id = (int)EventTypeIds.AccessDenied, + ModuleId = moduleId, + Name = "Access Denied", + Format = "User: {0}; Resource: {1}; Reason: {2}", + Severity = (int)LogEventType.Severities.Warning, + UseLive = true, UsePersist = true, UseDisplay = true + }, + new LogEventType() { + Id = (int)EventTypeIds.RoleCreated, + ModuleId = moduleId, + Name = "Authorization Role Created", + Format = "{1} [{0}] was created by {2}", + Severity = (int)LogEventType.Severities.Information, + UseLive = true, UsePersist = true, UseDisplay = true + }, + new LogEventType() { + Id = (int)EventTypeIds.RoleDeleted, + ModuleId = moduleId, + Name = "Authorization Role Deleted", + Format = "{1} [{0}] was deleted by {2}", + Severity = (int)LogEventType.Severities.Information, + UseLive = true, UsePersist = true, UseDisplay = true + }, + new LogEventType() { + Id = (int)EventTypeIds.RoleConfiguredRenamed, + ModuleId = moduleId, + Name = "Authorization Role Renamed", + Format = "{1} [{0}] was renamed by {2}: from {3}", + Severity = (int)LogEventType.Severities.Information, + UseLive = true, UsePersist = true, UseDisplay = true + }, + new LogEventType() { + Id = (int)EventTypeIds.RoleConfiguredSubjectsAdded, + ModuleId = moduleId, + Name = "Authorization Role Subjects Added", + Format = "{1} [{0}] had subjects added by {2}: {3}", + Severity = (int)LogEventType.Severities.Information, + UseLive = true, UsePersist = true, UseDisplay = true + }, + new LogEventType() { + Id = (int)EventTypeIds.RoleConfiguredSubjectsRemoved, + ModuleId = moduleId, + Name = "Authorization Role Subjects Removed", + Format = "{1} [{0}] had subjects removed by {2}: {3}", + Severity = (int)LogEventType.Severities.Information, + UseLive = true, UsePersist = true, UseDisplay = true + }, + new LogEventType() { + Id = (int)EventTypeIds.RoleConfiguredClaimsAdded, + ModuleId = moduleId, + Name = "Authorization Role Claims Added", + Format = "{1} [{0}] had claims added by {2}: {3}", + Severity = (int)LogEventType.Severities.Information, + UseLive = true, UsePersist = true, UseDisplay = true + }, + new LogEventType() { + Id = (int)EventTypeIds.RoleConfiguredClaimsRemoved, + ModuleId = moduleId, + Name = "Authorization Role Claims Removed", + Format = "{1} [{0}] had claims removed by {2}: {3}", + Severity = (int)LogEventType.Severities.Information, + UseLive = true, UsePersist = true, UseDisplay = true + } + }; + + return eventTypes; + } + + #region Log Helpers + public static void LogAccessDenied(string UserId, string Resource, string Reason) + { + Log(EventTypeIds.AccessDenied, UserId ?? "", Resource, Reason); + } + public static void LogRoleCreated(AuthorizationRole Role, string UserId) + { + Log(EventTypeIds.RoleCreated, Role.Id, Role.Name, UserId); + } + public static void LogRoleDeleted(AuthorizationRole Role, string UserId) + { + Log(EventTypeIds.RoleDeleted, Role.Id, Role.Name, UserId); + } + public static void LogRoleConfiguredRenamed(AuthorizationRole Role, string UserId, string OldRoleName) + { + Log(EventTypeIds.RoleConfiguredRenamed, Role.Id, Role.Name, UserId, OldRoleName); + } + public static void LogRoleConfiguredSubjectsAdded(AuthorizationRole Role, string UserId, IEnumerable SubjectsAdded) + { + var subjects = string.Join("; ", SubjectsAdded); + Log(EventTypeIds.RoleConfiguredSubjectsAdded, Role.Id, Role.Name, UserId, subjects); + } + public static void LogRoleConfiguredSubjectsRemoved(AuthorizationRole Role, string UserId, IEnumerable SubjectsRemoved) + { + var subjects = string.Join("; ", SubjectsRemoved); + Log(EventTypeIds.RoleConfiguredSubjectsRemoved, Role.Id, Role.Name, UserId, subjects); + } + public static void LogRoleConfiguredClaimsAdded(AuthorizationRole Role, string UserId, IEnumerable ClaimsAdded) + { + var claims = string.Join("; ", ClaimsAdded); + Log(EventTypeIds.RoleConfiguredClaimsAdded, Role.Id, Role.Name, UserId, claims); + } + public static void LogRoleConfiguredClaimsRemoved(AuthorizationRole Role, string UserId, IEnumerable ClaimsRemoved) + { + var claims = string.Join("; ", ClaimsRemoved); + Log(EventTypeIds.RoleConfiguredClaimsRemoved, Role.Id, Role.Name, UserId, claims); + } + #endregion + } +} diff --git a/Disco.Services/Authorization/AuthorizationToken.cs b/Disco.Services/Authorization/AuthorizationToken.cs index a9812c56..3b08c5e5 100644 --- a/Disco.Services/Authorization/AuthorizationToken.cs +++ b/Disco.Services/Authorization/AuthorizationToken.cs @@ -3,6 +3,7 @@ using Disco.Models.Repository; using Disco.Services.Authorization.Roles; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -133,7 +134,7 @@ namespace Disco.Services.Authorization public void Require(string ClaimKey) { if (!Has(ClaimKey)) - throw new AccessDeniedException(BuildRequireMessage(ClaimKey)); + throw new AccessDeniedException(BuildRequireMessage(ClaimKey), GetRequireResource()); } /// @@ -143,7 +144,7 @@ namespace Disco.Services.Authorization public void RequireAll(params string[] ClaimKeys) { if (!HasAll(ClaimKeys)) - throw new AccessDeniedException(BuildRequireAllMessage(ClaimKeys)); + throw new AccessDeniedException(BuildRequireAllMessage(ClaimKeys), GetRequireResource()); } /// @@ -153,7 +154,29 @@ namespace Disco.Services.Authorization public void RequireAny(params string[] ClaimKeys) { if (!HasAny(ClaimKeys)) - throw new AccessDeniedException(BuildRequireAnyMessage(ClaimKeys)); + throw new AccessDeniedException(BuildRequireAnyMessage(ClaimKeys), GetRequireResource()); + } + + private string GetRequireResource() + { + var stackTrace = new StackTrace(2, true); + if (stackTrace.FrameCount > 1) + { + var frame = stackTrace.GetFrame(0); + + // Filename + var filename = frame.GetFileName(); + if (!string.IsNullOrEmpty(filename) && filename.Contains("\\Disco\\Disco.")) + filename = filename.Substring(filename.IndexOf("\\Disco\\Disco.") + 7); + + var method = frame.GetMethod(); + var resource = string.Format("{0}::{1}", method.DeclaringType.FullName, method.Name); + if (!string.IsNullOrEmpty(filename)) + resource = string.Format("{0} [{1}]", resource, filename); + + return resource; + } + return "[Unknown]"; } #endregion diff --git a/Disco.Services/Authorization/Claims.cs b/Disco.Services/Authorization/Claims.cs index 829f4c91..b56d0957 100644 --- a/Disco.Services/Authorization/Claims.cs +++ b/Disco.Services/Authorization/Claims.cs @@ -10,174 +10,175 @@ using Disco.Models.Repository; using Disco.Services.Authorization.Roles; using System; using System.Collections.Generic; +using System.Linq; namespace Disco.Services.Authorization { public static class Claims { - private static Dictionary, Action, string, string>> _roleClaims; + private static Dictionary, Action, string, string, bool>> _roleClaims; private static ClaimNavigatorItem _claimNavigator; static Claims() { #region Role Claim Dictionary - _roleClaims = new Dictionary, Action, string, string>>() + _roleClaims = new Dictionary, Action, string, string, bool>>() { - { "Config.DeviceCertificate.DownloadCertificates", new Tuple, Action, string, string>(c => c.Config.DeviceCertificate.DownloadCertificates, (c, v) => c.Config.DeviceCertificate.DownloadCertificates = v, "Download Certificates", "Can download certificates") }, - { "Config.Enrolment.Configure", new Tuple, Action, string, string>(c => c.Config.Enrolment.Configure, (c, v) => c.Config.Enrolment.Configure = v, "Configure Enrolment", "Can configure device enrolment") }, - { "Config.Enrolment.DownloadBootstrapper", new Tuple, Action, string, string>(c => c.Config.Enrolment.DownloadBootstrapper, (c, v) => c.Config.Enrolment.DownloadBootstrapper = v, "Download Bootstrapper", "Can download the Device Bootstrapper") }, - { "Config.Enrolment.Show", new Tuple, Action, string, string>(c => c.Config.Enrolment.Show, (c, v) => c.Config.Enrolment.Show = v, "Show Enrolment", "Can show device enrolment") }, - { "Config.Enrolment.ShowStatus", new Tuple, Action, string, string>(c => c.Config.Enrolment.ShowStatus, (c, v) => c.Config.Enrolment.ShowStatus = v, "Show Enrolment Status", "Can show the enrolment status") }, - { "Config.DeviceBatch.Configure", new Tuple, Action, string, string>(c => c.Config.DeviceBatch.Configure, (c, v) => c.Config.DeviceBatch.Configure = v, "Configure Device Batches", "Can configure device batches") }, - { "Config.DeviceBatch.Create", new Tuple, Action, string, string>(c => c.Config.DeviceBatch.Create, (c, v) => c.Config.DeviceBatch.Create = v, "Create Device Batches", "Can create device batches") }, - { "Config.DeviceBatch.Delete", new Tuple, Action, string, string>(c => c.Config.DeviceBatch.Delete, (c, v) => c.Config.DeviceBatch.Delete = v, "Delete Device Batches", "Can delete device batches") }, - { "Config.DeviceBatch.Show", new Tuple, Action, string, string>(c => c.Config.DeviceBatch.Show, (c, v) => c.Config.DeviceBatch.Show = v, "Show Device Batches", "Can show device batches") }, - { "Config.DeviceBatch.ShowTimeline", new Tuple, Action, string, string>(c => c.Config.DeviceBatch.ShowTimeline, (c, v) => c.Config.DeviceBatch.ShowTimeline = v, "Show Timeline", "Can show device batch timeline") }, - { "Config.DeviceModel.ConfigureComponents", new Tuple, Action, string, string>(c => c.Config.DeviceModel.ConfigureComponents, (c, v) => c.Config.DeviceModel.ConfigureComponents = v, "Configure Device Model Components", "Can configure device model components") }, - { "Config.DeviceModel.Configure", new Tuple, Action, string, string>(c => c.Config.DeviceModel.Configure, (c, v) => c.Config.DeviceModel.Configure = v, "Configure Device Models", "Can configure device models") }, - { "Config.DeviceModel.Delete", new Tuple, Action, string, string>(c => c.Config.DeviceModel.Delete, (c, v) => c.Config.DeviceModel.Delete = v, "Delete Device Models", "Can delete device models") }, - { "Config.DeviceModel.Show", new Tuple, Action, string, string>(c => c.Config.DeviceModel.Show, (c, v) => c.Config.DeviceModel.Show = v, "Show Device Models", "Can show device models") }, - { "Config.DeviceProfile.ConfigureComputerNameTemplate", new Tuple, Action, string, string>(c => c.Config.DeviceProfile.ConfigureComputerNameTemplate, (c, v) => c.Config.DeviceProfile.ConfigureComputerNameTemplate = v, "Configure Computer Name Templates", "Can configure computer name templates for device profiles") }, - { "Config.DeviceProfile.ConfigureDefaults", new Tuple, Action, string, string>(c => c.Config.DeviceProfile.ConfigureDefaults, (c, v) => c.Config.DeviceProfile.ConfigureDefaults = v, "Configure Default Device Profiles", "Can configure default device profiles") }, - { "Config.DeviceProfile.Configure", new Tuple, Action, string, string>(c => c.Config.DeviceProfile.Configure, (c, v) => c.Config.DeviceProfile.Configure = v, "Configure Device Profiles", "Can configure device profiles") }, - { "Config.DeviceProfile.Create", new Tuple, Action, string, string>(c => c.Config.DeviceProfile.Create, (c, v) => c.Config.DeviceProfile.Create = v, "Create Device Profiles", "Can create device profiles") }, - { "Config.DeviceProfile.Delete", new Tuple, Action, string, string>(c => c.Config.DeviceProfile.Delete, (c, v) => c.Config.DeviceProfile.Delete = v, "Delete Device Profiles", "Can delete device profiles") }, - { "Config.DeviceProfile.Show", new Tuple, Action, string, string>(c => c.Config.DeviceProfile.Show, (c, v) => c.Config.DeviceProfile.Show = v, "Show Device Profiles", "Can show device profiles") }, - { "Config.DocumentTemplate.BulkGenerate", new Tuple, Action, string, string>(c => c.Config.DocumentTemplate.BulkGenerate, (c, v) => c.Config.DocumentTemplate.BulkGenerate = v, "Bulk Generate Document Templates", "Can bulk generate document templates") }, - { "Config.DocumentTemplate.Configure", new Tuple, Action, string, string>(c => c.Config.DocumentTemplate.Configure, (c, v) => c.Config.DocumentTemplate.Configure = v, "Configure Document Templates", "Can configure document templates") }, - { "Config.DocumentTemplate.ConfigureFilterExpression", new Tuple, Action, string, string>(c => c.Config.DocumentTemplate.ConfigureFilterExpression, (c, v) => c.Config.DocumentTemplate.ConfigureFilterExpression = v, "Configure Filter Expression", "Can configure filter expressions for document templates") }, - { "Config.DocumentTemplate.Create", new Tuple, Action, string, string>(c => c.Config.DocumentTemplate.Create, (c, v) => c.Config.DocumentTemplate.Create = v, "Create Document Templates", "Can create document templates") }, - { "Config.DocumentTemplate.Delete", new Tuple, Action, string, string>(c => c.Config.DocumentTemplate.Delete, (c, v) => c.Config.DocumentTemplate.Delete = v, "Delete Document Templates", "Can delete document templates") }, - { "Config.DocumentTemplate.ShowStatus", new Tuple, Action, string, string>(c => c.Config.DocumentTemplate.ShowStatus, (c, v) => c.Config.DocumentTemplate.ShowStatus = v, "Show Document Template Import Status", "Can show the document template import status") }, - { "Config.DocumentTemplate.Show", new Tuple, Action, string, string>(c => c.Config.DocumentTemplate.Show, (c, v) => c.Config.DocumentTemplate.Show = v, "Show Document Templates", "Can show document templates") }, - { "Config.DocumentTemplate.UndetectedPages", new Tuple, Action, string, string>(c => c.Config.DocumentTemplate.UndetectedPages, (c, v) => c.Config.DocumentTemplate.UndetectedPages = v, "Undetected Pages", "Can show and assign imported documents which were not undetected") }, - { "Config.DocumentTemplate.Upload", new Tuple, Action, string, string>(c => c.Config.DocumentTemplate.Upload, (c, v) => c.Config.DocumentTemplate.Upload = v, "Upload Document Templates", "Can upload document templates") }, - { "Config.Logging.Show", new Tuple, Action, string, string>(c => c.Config.Logging.Show, (c, v) => c.Config.Logging.Show = v, "Show Logging", "Can show logging") }, - { "Config.Plugin.Configure", new Tuple, Action, string, string>(c => c.Config.Plugin.Configure, (c, v) => c.Config.Plugin.Configure = v, "Configure Plugins", "Can configure plugins") }, - { "Config.Plugin.InstallLocal", new Tuple, Action, string, string>(c => c.Config.Plugin.InstallLocal, (c, v) => c.Config.Plugin.InstallLocal = v, "Install/Update Local Plugins", "Can install and update locally uploaded plugins") }, - { "Config.Plugin.Install", new Tuple, Action, string, string>(c => c.Config.Plugin.Install, (c, v) => c.Config.Plugin.Install = v, "Install/Update Plugins", "Can install and update plugins") }, - { "Config.Plugin.Show", new Tuple, Action, string, string>(c => c.Config.Plugin.Show, (c, v) => c.Config.Plugin.Show = v, "Show Plugins", "Can show plugins") }, - { "Config.Plugin.Uninstall", new Tuple, Action, string, string>(c => c.Config.Plugin.Uninstall, (c, v) => c.Config.Plugin.Uninstall = v, "Uninstall Plugins", "Can uninstall plugins") }, - { "Config.System.ConfigureProxy", new Tuple, Action, string, string>(c => c.Config.System.ConfigureProxy, (c, v) => c.Config.System.ConfigureProxy = v, "Configure Proxy Settings", "Can configure the proxy settings") }, - { "Config.System.Show", new Tuple, Action, string, string>(c => c.Config.System.Show, (c, v) => c.Config.System.Show = v, "Show System Configuration", "Can show the system configuration") }, - { "Config.Organisation.ConfigureAddresses", new Tuple, Action, string, string>(c => c.Config.Organisation.ConfigureAddresses, (c, v) => c.Config.Organisation.ConfigureAddresses = v, "Configure Addresses", "Can configure organisation addresses") }, - { "Config.Organisation.ConfigureLogo", new Tuple, Action, string, string>(c => c.Config.Organisation.ConfigureLogo, (c, v) => c.Config.Organisation.ConfigureLogo = v, "Configure Logo", "Can configure the organisation logo") }, - { "Config.Organisation.ConfigureMultiSiteMode", new Tuple, Action, string, string>(c => c.Config.Organisation.ConfigureMultiSiteMode, (c, v) => c.Config.Organisation.ConfigureMultiSiteMode = v, "Configure Multi-Site Mode", "Can configure multi-site mode") }, - { "Config.Organisation.ConfigureName", new Tuple, Action, string, string>(c => c.Config.Organisation.ConfigureName, (c, v) => c.Config.Organisation.ConfigureName = v, "Configure Name", "Can configure the organisation name") }, - { "Config.Organisation.Show", new Tuple, Action, string, string>(c => c.Config.Organisation.Show, (c, v) => c.Config.Organisation.Show = v, "Show Organisation Details", "Can show the organisation details") }, - { "Config.Show", new Tuple, Action, string, string>(c => c.Config.Show, (c, v) => c.Config.Show = v, "Show Configuration", "Can show the configuration menu") }, - { "Job.Lists.AllOpen", new Tuple, Action, string, string>(c => c.Job.Lists.AllOpen, (c, v) => c.Job.Lists.AllOpen = v, "All Open List", "Can show list") }, - { "Job.Lists.AwaitingFinanceAgreementBreach", new Tuple, Action, string, string>(c => c.Job.Lists.AwaitingFinanceAgreementBreach, (c, v) => c.Job.Lists.AwaitingFinanceAgreementBreach = v, "Awaiting Finance Agreement Breach List", "Can show list (NOTE: Requires Awaiting Finance List)") }, - { "Job.Lists.AwaitingFinanceCharge", new Tuple, Action, string, string>(c => c.Job.Lists.AwaitingFinanceCharge, (c, v) => c.Job.Lists.AwaitingFinanceCharge = v, "Awaiting Finance Charge List", "Can show list (NOTE: Requires Awaiting Finance List)") }, - { "Job.Lists.AwaitingFinanceInsuranceProcessing", new Tuple, Action, string, string>(c => c.Job.Lists.AwaitingFinanceInsuranceProcessing, (c, v) => c.Job.Lists.AwaitingFinanceInsuranceProcessing = v, "Awaiting Finance Insurance Processing List", "Can show list (NOTE: Requires Awaiting Finance List)") }, - { "Job.Lists.AwaitingFinance", new Tuple, Action, string, string>(c => c.Job.Lists.AwaitingFinance, (c, v) => c.Job.Lists.AwaitingFinance = v, "Awaiting Finance List", "Can show list") }, - { "Job.Lists.AwaitingFinancePayment", new Tuple, Action, string, string>(c => c.Job.Lists.AwaitingFinancePayment, (c, v) => c.Job.Lists.AwaitingFinancePayment = v, "Awaiting Finance Payment List", "Can show list (NOTE: Requires Awaiting Finance List)") }, - { "Job.Lists.AwaitingTechnicianAction", new Tuple, Action, string, string>(c => c.Job.Lists.AwaitingTechnicianAction, (c, v) => c.Job.Lists.AwaitingTechnicianAction = v, "Awaiting Technician Action List", "Can show list") }, - { "Job.Lists.AwaitingUserAction", new Tuple, Action, string, string>(c => c.Job.Lists.AwaitingUserAction, (c, v) => c.Job.Lists.AwaitingUserAction = v, "Awaiting User Action List", "Can show list") }, - { "Job.Lists.DevicesAwaitingRepair", new Tuple, Action, string, string>(c => c.Job.Lists.DevicesAwaitingRepair, (c, v) => c.Job.Lists.DevicesAwaitingRepair = v, "Devices Awaiting Repair List", "Can show list") }, - { "Job.Lists.DevicesReadyForReturn", new Tuple, Action, string, string>(c => c.Job.Lists.DevicesReadyForReturn, (c, v) => c.Job.Lists.DevicesReadyForReturn = v, "Devices Ready For Return List", "Can show list") }, - { "Job.Lists.Locations", new Tuple, Action, string, string>(c => c.Job.Lists.Locations, (c, v) => c.Job.Lists.Locations = v, "Locations List", "Can show list") }, - { "Job.Lists.LongRunningJobs", new Tuple, Action, string, string>(c => c.Job.Lists.LongRunningJobs, (c, v) => c.Job.Lists.LongRunningJobs = v, "Long Running Jobs List", "Can show list") }, - { "Job.Lists.RecentlyClosed", new Tuple, Action, string, string>(c => c.Job.Lists.RecentlyClosed, (c, v) => c.Job.Lists.RecentlyClosed = v, "Recently Closed List", "Can show list") }, - { "Job.Actions.AddAttachments", new Tuple, Action, string, string>(c => c.Job.Actions.AddAttachments, (c, v) => c.Job.Actions.AddAttachments = v, "Add Attachments", "Can add attachments to jobs") }, - { "Job.Actions.AddLogs", new Tuple, Action, string, string>(c => c.Job.Actions.AddLogs, (c, v) => c.Job.Actions.AddLogs = v, "Add Logs", "Can add job logs") }, - { "Job.Actions.Close", new Tuple, Action, string, string>(c => c.Job.Actions.Close, (c, v) => c.Job.Actions.Close = v, "Close Jobs", "Can close jobs") }, - { "Job.Actions.ConvertHWarToHNWar", new Tuple, Action, string, string>(c => c.Job.Actions.ConvertHWarToHNWar, (c, v) => c.Job.Actions.ConvertHWarToHNWar = v, "Convert HWar Jobs To HNWar", "Can convert warranty jobs to non-warranty jobs") }, - { "Job.Actions.Create", new Tuple, Action, string, string>(c => c.Job.Actions.Create, (c, v) => c.Job.Actions.Create = v, "Create Jobs", "Can create jobs") }, - { "Job.Actions.Delete", new Tuple, Action, string, string>(c => c.Job.Actions.Delete, (c, v) => c.Job.Actions.Delete = v, "Delete Jobs", "Can delete jobs") }, - { "Job.Actions.ForceClose", new Tuple, Action, string, string>(c => c.Job.Actions.ForceClose, (c, v) => c.Job.Actions.ForceClose = v, "Force Close Jobs", "Can force close jobs") }, - { "Job.Actions.GenerateDocuments", new Tuple, Action, string, string>(c => c.Job.Actions.GenerateDocuments, (c, v) => c.Job.Actions.GenerateDocuments = v, "Generate Documents", "Can generate documents for jobs") }, - { "Job.Actions.LogRepair", new Tuple, Action, string, string>(c => c.Job.Actions.LogRepair, (c, v) => c.Job.Actions.LogRepair = v, "Log Repair", "Can log repair for non-warranty jobs") }, - { "Job.Actions.LogWarranty", new Tuple, Action, string, string>(c => c.Job.Actions.LogWarranty, (c, v) => c.Job.Actions.LogWarranty = v, "Log Warranty", "Can log warranty for jobs") }, - { "Job.Actions.RemoveAnyAttachments", new Tuple, Action, string, string>(c => c.Job.Actions.RemoveAnyAttachments, (c, v) => c.Job.Actions.RemoveAnyAttachments = v, "Remove Any Attachments", "Can remove any attachments from jobs") }, - { "Job.Actions.RemoveAnyLogs", new Tuple, Action, string, string>(c => c.Job.Actions.RemoveAnyLogs, (c, v) => c.Job.Actions.RemoveAnyLogs = v, "Remove Any Logs", "Can remove any job logs") }, - { "Job.Actions.RemoveOwnAttachments", new Tuple, Action, string, string>(c => c.Job.Actions.RemoveOwnAttachments, (c, v) => c.Job.Actions.RemoveOwnAttachments = v, "Remove Own Attachments", "Can remove own attachments from jobs") }, - { "Job.Actions.RemoveOwnLogs", new Tuple, Action, string, string>(c => c.Job.Actions.RemoveOwnLogs, (c, v) => c.Job.Actions.RemoveOwnLogs = v, "Remove Own Logs", "Can remove own job logs") }, - { "Job.Actions.Reopen", new Tuple, Action, string, string>(c => c.Job.Actions.Reopen, (c, v) => c.Job.Actions.Reopen = v, "Reopen Jobs", "Can reopen jobs") }, - { "Job.Actions.UpdateSubTypes", new Tuple, Action, string, string>(c => c.Job.Actions.UpdateSubTypes, (c, v) => c.Job.Actions.UpdateSubTypes = v, "Update Sub Types", "Can update sub types for jobs") }, - { "Job.Properties.WarrantyProperties.ExternalCompletedDate", new Tuple, Action, string, string>(c => c.Job.Properties.WarrantyProperties.ExternalCompletedDate, (c, v) => c.Job.Properties.WarrantyProperties.ExternalCompletedDate = v, "External Completed Date Property", "Can update property") }, - { "Job.Properties.WarrantyProperties.ExternalLoggedDate", new Tuple, Action, string, string>(c => c.Job.Properties.WarrantyProperties.ExternalLoggedDate, (c, v) => c.Job.Properties.WarrantyProperties.ExternalLoggedDate = v, "External Logged Date Property", "Can update property") }, - { "Job.Properties.WarrantyProperties.ExternalName", new Tuple, Action, string, string>(c => c.Job.Properties.WarrantyProperties.ExternalName, (c, v) => c.Job.Properties.WarrantyProperties.ExternalName = v, "External Name Property", "Can update property") }, - { "Job.Properties.WarrantyProperties.ExternalReference", new Tuple, Action, string, string>(c => c.Job.Properties.WarrantyProperties.ExternalReference, (c, v) => c.Job.Properties.WarrantyProperties.ExternalReference = v, "External Reference Property", "Can update property") }, - { "Job.Properties.WarrantyProperties.ProviderDetails", new Tuple, Action, string, string>(c => c.Job.Properties.WarrantyProperties.ProviderDetails, (c, v) => c.Job.Properties.WarrantyProperties.ProviderDetails = v, "Provider Details", "Can access warranty provider details") }, - { "Job.Properties.WarrantyProperties.WarrantyCompleted", new Tuple, Action, string, string>(c => c.Job.Properties.WarrantyProperties.WarrantyCompleted, (c, v) => c.Job.Properties.WarrantyProperties.WarrantyCompleted = v, "Warranty Completed Property", "Can update property") }, - { "Job.Properties.NonWarrantyProperties.AccountingChargeAdded", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.AccountingChargeAdded, (c, v) => c.Job.Properties.NonWarrantyProperties.AccountingChargeAdded = v, "Accounting Charge Added Property", "Can update property") }, - { "Job.Properties.NonWarrantyProperties.AccountingChargePaid", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.AccountingChargePaid, (c, v) => c.Job.Properties.NonWarrantyProperties.AccountingChargePaid = v, "Accounting Charge Paid Property", "Can update property") }, - { "Job.Properties.NonWarrantyProperties.AccountingChargeRequired", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.AccountingChargeRequired, (c, v) => c.Job.Properties.NonWarrantyProperties.AccountingChargeRequired = v, "Accounting Charge Required Property", "Can update property") }, - { "Job.Properties.NonWarrantyProperties.AddComponents", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.AddComponents, (c, v) => c.Job.Properties.NonWarrantyProperties.AddComponents = v, "Add Components", "Can add job components (NOTE: Requires Edit Components)") }, - { "Job.Properties.NonWarrantyProperties.EditComponents", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.EditComponents, (c, v) => c.Job.Properties.NonWarrantyProperties.EditComponents = v, "Edit Components", "Can edit and remove job components") }, - { "Job.Properties.NonWarrantyProperties.InsuranceClaimFormSent", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.InsuranceClaimFormSent, (c, v) => c.Job.Properties.NonWarrantyProperties.InsuranceClaimFormSent = v, "Insurance Claim Form Sent Property", "Can update property") }, - { "Job.Properties.NonWarrantyProperties.InsuranceDetails", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.InsuranceDetails, (c, v) => c.Job.Properties.NonWarrantyProperties.InsuranceDetails = v, "Insurance Detail Properties", "Can update insurance detail properties") }, - { "Job.Properties.NonWarrantyProperties.InvoiceReceived", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.InvoiceReceived, (c, v) => c.Job.Properties.NonWarrantyProperties.InvoiceReceived = v, "Invoice Received Property", "Can update property") }, - { "Job.Properties.NonWarrantyProperties.IsInsuranceClaim", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.IsInsuranceClaim, (c, v) => c.Job.Properties.NonWarrantyProperties.IsInsuranceClaim = v, "Is Insurance Claim Property", "Can update property") }, - { "Job.Properties.NonWarrantyProperties.PurchaseOrderRaised", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.PurchaseOrderRaised, (c, v) => c.Job.Properties.NonWarrantyProperties.PurchaseOrderRaised = v, "Purchase Order Raised Property", "Can update property") }, - { "Job.Properties.NonWarrantyProperties.PurchaseOrderReference", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.PurchaseOrderReference, (c, v) => c.Job.Properties.NonWarrantyProperties.PurchaseOrderReference = v, "Purchase Order Reference Property", "Can update property") }, - { "Job.Properties.NonWarrantyProperties.PurchaseOrderSent", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.PurchaseOrderSent, (c, v) => c.Job.Properties.NonWarrantyProperties.PurchaseOrderSent = v, "Purchase Order Sent Property", "Can update property") }, - { "Job.Properties.NonWarrantyProperties.RepairerCompletedDate", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.RepairerCompletedDate, (c, v) => c.Job.Properties.NonWarrantyProperties.RepairerCompletedDate = v, "Repairer Completed Date Property", "Can update property") }, - { "Job.Properties.NonWarrantyProperties.RepairerLoggedDate", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.RepairerLoggedDate, (c, v) => c.Job.Properties.NonWarrantyProperties.RepairerLoggedDate = v, "Repairer Logged Date Property", "Can update property") }, - { "Job.Properties.NonWarrantyProperties.RepairerName", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.RepairerName, (c, v) => c.Job.Properties.NonWarrantyProperties.RepairerName = v, "Repairer Name Property", "Can update property") }, - { "Job.Properties.NonWarrantyProperties.RepairerReference", new Tuple, Action, string, string>(c => c.Job.Properties.NonWarrantyProperties.RepairerReference, (c, v) => c.Job.Properties.NonWarrantyProperties.RepairerReference = v, "Repairer Reference Property", "Can update property") }, - { "Job.Properties.DeviceHeldLocation", new Tuple, Action, string, string>(c => c.Job.Properties.DeviceHeldLocation, (c, v) => c.Job.Properties.DeviceHeldLocation = v, "Device Held Location Property", "Can update property") }, - { "Job.Properties.DeviceHeld", new Tuple, Action, string, string>(c => c.Job.Properties.DeviceHeld, (c, v) => c.Job.Properties.DeviceHeld = v, "Device Held Property", "Can update property") }, - { "Job.Properties.DeviceReadyForReturn", new Tuple, Action, string, string>(c => c.Job.Properties.DeviceReadyForReturn, (c, v) => c.Job.Properties.DeviceReadyForReturn = v, "Device Ready For Return Property", "Can update property") }, - { "Job.Properties.DeviceReturned", new Tuple, Action, string, string>(c => c.Job.Properties.DeviceReturned, (c, v) => c.Job.Properties.DeviceReturned = v, "Device Returned Property", "Can update property") }, - { "Job.Properties.ExpectedClosedDate", new Tuple, Action, string, string>(c => c.Job.Properties.ExpectedClosedDate, (c, v) => c.Job.Properties.ExpectedClosedDate = v, "Expected Closed Date Property", "Can update property") }, - { "Job.Properties.Flags", new Tuple, Action, string, string>(c => c.Job.Properties.Flags, (c, v) => c.Job.Properties.Flags = v, "Flags Property", "Can update property") }, - { "Job.Properties.NotWaitingForUserAction", new Tuple, Action, string, string>(c => c.Job.Properties.NotWaitingForUserAction, (c, v) => c.Job.Properties.NotWaitingForUserAction = v, "Not Waiting For User Action Property", "Can update property") }, - { "Job.Properties.WaitingForUserAction", new Tuple, Action, string, string>(c => c.Job.Properties.WaitingForUserAction, (c, v) => c.Job.Properties.WaitingForUserAction = v, "Waiting For User Action Property", "Can update property") }, - { "Job.Types.ShowHMisc", new Tuple, Action, string, string>(c => c.Job.Types.ShowHMisc, (c, v) => c.Job.Types.ShowHMisc = v, "Show Hardware - Miscellaneous Jobs", "Can show jobs of this type") }, - { "Job.Types.ShowHNWar", new Tuple, Action, string, string>(c => c.Job.Types.ShowHNWar, (c, v) => c.Job.Types.ShowHNWar = v, "Show Hardware - Non-Warranty Jobs", "Can show jobs of this type") }, - { "Job.Types.ShowHWar", new Tuple, Action, string, string>(c => c.Job.Types.ShowHWar, (c, v) => c.Job.Types.ShowHWar = v, "Show Hardware - Warranty Jobs", "Can show jobs of this type") }, - { "Job.Types.ShowSApp", new Tuple, Action, string, string>(c => c.Job.Types.ShowSApp, (c, v) => c.Job.Types.ShowSApp = v, "Show Software - Application Jobs", "Can show jobs of this type") }, - { "Job.Types.ShowSOS", new Tuple, Action, string, string>(c => c.Job.Types.ShowSOS, (c, v) => c.Job.Types.ShowSOS = v, "Show Software - Operating System Jobs", "Can show jobs of this type") }, - { "Job.Types.ShowSImg", new Tuple, Action, string, string>(c => c.Job.Types.ShowSImg, (c, v) => c.Job.Types.ShowSImg = v, "Show Software - Reimage Jobs", "Can show jobs of this type") }, - { "Job.Types.ShowUMgmt", new Tuple, Action, string, string>(c => c.Job.Types.ShowUMgmt, (c, v) => c.Job.Types.ShowUMgmt = v, "Show User Management Jobs", "Can show jobs of this type") }, - { "Job.Search", new Tuple, Action, string, string>(c => c.Job.Search, (c, v) => c.Job.Search = v, "Search Jobs", "Can search jobs") }, - { "Job.ShowAttachments", new Tuple, Action, string, string>(c => c.Job.ShowAttachments, (c, v) => c.Job.ShowAttachments = v, "Show Attachments", "Can show job attachments") }, - { "Job.ShowDailyChart", new Tuple, Action, string, string>(c => c.Job.ShowDailyChart, (c, v) => c.Job.ShowDailyChart = v, "Show Daily Opened & Closed", "Can show daily opened & closed chart") }, - { "Job.ShowFlags", new Tuple, Action, string, string>(c => c.Job.ShowFlags, (c, v) => c.Job.ShowFlags = v, "Show Flags", "Can show job flags") }, - { "Job.Show", new Tuple, Action, string, string>(c => c.Job.Show, (c, v) => c.Job.Show = v, "Show Jobs", "Can show jobs") }, - { "Job.ShowLogs", new Tuple, Action, string, string>(c => c.Job.ShowLogs, (c, v) => c.Job.ShowLogs = v, "Show Logs", "Can show job logs") }, - { "Job.ShowNonWarrantyComponents", new Tuple, Action, string, string>(c => c.Job.ShowNonWarrantyComponents, (c, v) => c.Job.ShowNonWarrantyComponents = v, "Show Non-Warranty Components", "Can show non-warranty job components") }, - { "Job.ShowNonWarrantyFinance", new Tuple, Action, string, string>(c => c.Job.ShowNonWarrantyFinance, (c, v) => c.Job.ShowNonWarrantyFinance = v, "Show Non-Warranty Finance", "Can show non-warranty job finance") }, - { "Job.ShowNonWarrantyInsurance", new Tuple, Action, string, string>(c => c.Job.ShowNonWarrantyInsurance, (c, v) => c.Job.ShowNonWarrantyInsurance = v, "Show Non-Warranty Insurance", "Can show non-warranty job insurance") }, - { "Job.ShowNonWarrantyRepairs", new Tuple, Action, string, string>(c => c.Job.ShowNonWarrantyRepairs, (c, v) => c.Job.ShowNonWarrantyRepairs = v, "Show Non-Warranty Repairs", "Can show non-warranty job repairs") }, - { "Job.ShowWarranty", new Tuple, Action, string, string>(c => c.Job.ShowWarranty, (c, v) => c.Job.ShowWarranty = v, "Show Warranty", "Can show job warranty") }, - { "Device.Properties.AssetNumber", new Tuple, Action, string, string>(c => c.Device.Properties.AssetNumber, (c, v) => c.Device.Properties.AssetNumber = v, "Asset Number Property", "Can update property") }, - { "Device.Properties.DeviceBatch", new Tuple, Action, string, string>(c => c.Device.Properties.DeviceBatch, (c, v) => c.Device.Properties.DeviceBatch = v, "Device Batch Property", "Can update property") }, - { "Device.Properties.DeviceProfile", new Tuple, Action, string, string>(c => c.Device.Properties.DeviceProfile, (c, v) => c.Device.Properties.DeviceProfile = v, "Device Profile Property", "Can update property") }, - { "Device.Properties.Location", new Tuple, Action, string, string>(c => c.Device.Properties.Location, (c, v) => c.Device.Properties.Location = v, "Location Property", "Can update property") }, - { "Device.Actions.AddAttachments", new Tuple, Action, string, string>(c => c.Device.Actions.AddAttachments, (c, v) => c.Device.Actions.AddAttachments = v, "Add Attachments", "Can add attachments to devices") }, - { "Device.Actions.AllowUnauthenticatedEnrol", new Tuple, Action, string, string>(c => c.Device.Actions.AllowUnauthenticatedEnrol, (c, v) => c.Device.Actions.AllowUnauthenticatedEnrol = v, "Allow Unauthenticated Enrol", "Can allow devices to enrol without authentication") }, - { "Device.Actions.AssignUser", new Tuple, Action, string, string>(c => c.Device.Actions.AssignUser, (c, v) => c.Device.Actions.AssignUser = v, "Assign User", "Can update the user assignment of devices") }, - { "Device.Actions.Decommission", new Tuple, Action, string, string>(c => c.Device.Actions.Decommission, (c, v) => c.Device.Actions.Decommission = v, "Decommission", "Can decommission devices") }, - { "Device.Actions.Delete", new Tuple, Action, string, string>(c => c.Device.Actions.Delete, (c, v) => c.Device.Actions.Delete = v, "Delete", "Can delete devices") }, - { "Device.Actions.EnrolDevices", new Tuple, Action, string, string>(c => c.Device.Actions.EnrolDevices, (c, v) => c.Device.Actions.EnrolDevices = v, "Enrol Devices", "Can add devices offline and enrol devices with the Bootstrapper") }, - { "Device.Actions.Export", new Tuple, Action, string, string>(c => c.Device.Actions.Export, (c, v) => c.Device.Actions.Export = v, "Export Devices", "Can export devices in a bulk format") }, - { "Device.Actions.GenerateDocuments", new Tuple, Action, string, string>(c => c.Device.Actions.GenerateDocuments, (c, v) => c.Device.Actions.GenerateDocuments = v, "Generate Documents", "Can generate documents for jobs") }, - { "Device.Actions.Import", new Tuple, Action, string, string>(c => c.Device.Actions.Import, (c, v) => c.Device.Actions.Import = v, "Import Devices", "Can bulk import devices") }, - { "Device.Actions.Recommission", new Tuple, Action, string, string>(c => c.Device.Actions.Recommission, (c, v) => c.Device.Actions.Recommission = v, "Recommission", "Can recommission devices") }, - { "Device.Actions.RemoveAnyAttachments", new Tuple, Action, string, string>(c => c.Device.Actions.RemoveAnyAttachments, (c, v) => c.Device.Actions.RemoveAnyAttachments = v, "Remove Any Attachments", "Can remove any attachments from devices") }, - { "Device.Actions.RemoveOwnAttachments", new Tuple, Action, string, string>(c => c.Device.Actions.RemoveOwnAttachments, (c, v) => c.Device.Actions.RemoveOwnAttachments = v, "Remove Own Attachments", "Can remove own attachments from devices") }, - { "Device.Search", new Tuple, Action, string, string>(c => c.Device.Search, (c, v) => c.Device.Search = v, "Search Devices", "Can search devices") }, - { "Device.ShowAssignmentHistory", new Tuple, Action, string, string>(c => c.Device.ShowAssignmentHistory, (c, v) => c.Device.ShowAssignmentHistory = v, "Show Assignment History", "Can show the assignment history for devices") }, - { "Device.ShowAttachments", new Tuple, Action, string, string>(c => c.Device.ShowAttachments, (c, v) => c.Device.ShowAttachments = v, "Show Attachments", "Can show device attachments") }, - { "Device.ShowCertificates", new Tuple, Action, string, string>(c => c.Device.ShowCertificates, (c, v) => c.Device.ShowCertificates = v, "Show Certificates", "Can show certificates associated with devices") }, - { "Device.ShowDetails", new Tuple, Action, string, string>(c => c.Device.ShowDetails, (c, v) => c.Device.ShowDetails = v, "Show Details", "Can show details associated with devices") }, - { "Device.Show", new Tuple, Action, string, string>(c => c.Device.Show, (c, v) => c.Device.Show = v, "Show Devices", "Can show devices") }, - { "Device.ShowJobs", new Tuple, Action, string, string>(c => c.Device.ShowJobs, (c, v) => c.Device.ShowJobs = v, "Show Devices Jobs", "Can show jobs associated with devices") }, - { "User.Actions.AddAttachments", new Tuple, Action, string, string>(c => c.User.Actions.AddAttachments, (c, v) => c.User.Actions.AddAttachments = v, "Add Attachments", "Can add attachments to users") }, - { "User.Actions.GenerateDocuments", new Tuple, Action, string, string>(c => c.User.Actions.GenerateDocuments, (c, v) => c.User.Actions.GenerateDocuments = v, "Generate Documents", "Can generate documents for users") }, - { "User.Actions.RemoveAnyAttachments", new Tuple, Action, string, string>(c => c.User.Actions.RemoveAnyAttachments, (c, v) => c.User.Actions.RemoveAnyAttachments = v, "Remove Any Attachments", "Can remove any attachments from users") }, - { "User.Actions.RemoveOwnAttachments", new Tuple, Action, string, string>(c => c.User.Actions.RemoveOwnAttachments, (c, v) => c.User.Actions.RemoveOwnAttachments = v, "Remove Own Attachments", "Can remove own attachments from users") }, - { "User.Search", new Tuple, Action, string, string>(c => c.User.Search, (c, v) => c.User.Search = v, "Search Users", "Can search users") }, - { "User.ShowAttachments", new Tuple, Action, string, string>(c => c.User.ShowAttachments, (c, v) => c.User.ShowAttachments = v, "Show Attachments", "Can show user attachments") }, - { "User.ShowAssignmentHistory", new Tuple, Action, string, string>(c => c.User.ShowAssignmentHistory, (c, v) => c.User.ShowAssignmentHistory = v, "Show Device Assignment History", "Can show the device assignment history for users") }, - { "User.Show", new Tuple, Action, string, string>(c => c.User.Show, (c, v) => c.User.Show = v, "Show Users", "Can show users") }, - { "User.ShowAuthorization", new Tuple, Action, string, string>(c => c.User.ShowAuthorization, (c, v) => c.User.ShowAuthorization = v, "Show Users Authorization", "Can show authorization permissions associated with users") }, - { "User.ShowJobs", new Tuple, Action, string, string>(c => c.User.ShowJobs, (c, v) => c.User.ShowJobs = v, "Show Users Jobs", "Can show jobs associated with users") }, - { "ComputerAccount", new Tuple, Action, string, string>(c => c.ComputerAccount, (c, v) => c.ComputerAccount = v, "Computer Account", "Represents a computer account") }, - { "DiscoAdminAccount", new Tuple, Action, string, string>(c => c.DiscoAdminAccount, (c, v) => c.DiscoAdminAccount = v, "Disco Administrator Account", "Represents a Disco Administrator account") } + { "Config.DeviceCertificate.DownloadCertificates", new Tuple, Action, string, string, bool>(c => c.Config.DeviceCertificate.DownloadCertificates, (c, v) => c.Config.DeviceCertificate.DownloadCertificates = v, "Download Certificates", "Can download certificates", false) }, + { "Config.Enrolment.Configure", new Tuple, Action, string, string, bool>(c => c.Config.Enrolment.Configure, (c, v) => c.Config.Enrolment.Configure = v, "Configure Enrolment", "Can configure device enrolment", false) }, + { "Config.Enrolment.DownloadBootstrapper", new Tuple, Action, string, string, bool>(c => c.Config.Enrolment.DownloadBootstrapper, (c, v) => c.Config.Enrolment.DownloadBootstrapper = v, "Download Bootstrapper", "Can download the Device Bootstrapper", false) }, + { "Config.Enrolment.Show", new Tuple, Action, string, string, bool>(c => c.Config.Enrolment.Show, (c, v) => c.Config.Enrolment.Show = v, "Show Enrolment", "Can show device enrolment", false) }, + { "Config.Enrolment.ShowStatus", new Tuple, Action, string, string, bool>(c => c.Config.Enrolment.ShowStatus, (c, v) => c.Config.Enrolment.ShowStatus = v, "Show Enrolment Status", "Can show the enrolment status", false) }, + { "Config.DeviceBatch.Configure", new Tuple, Action, string, string, bool>(c => c.Config.DeviceBatch.Configure, (c, v) => c.Config.DeviceBatch.Configure = v, "Configure Device Batches", "Can configure device batches", false) }, + { "Config.DeviceBatch.Create", new Tuple, Action, string, string, bool>(c => c.Config.DeviceBatch.Create, (c, v) => c.Config.DeviceBatch.Create = v, "Create Device Batches", "Can create device batches", false) }, + { "Config.DeviceBatch.Delete", new Tuple, Action, string, string, bool>(c => c.Config.DeviceBatch.Delete, (c, v) => c.Config.DeviceBatch.Delete = v, "Delete Device Batches", "Can delete device batches", false) }, + { "Config.DeviceBatch.Show", new Tuple, Action, string, string, bool>(c => c.Config.DeviceBatch.Show, (c, v) => c.Config.DeviceBatch.Show = v, "Show Device Batches", "Can show device batches", false) }, + { "Config.DeviceBatch.ShowTimeline", new Tuple, Action, string, string, bool>(c => c.Config.DeviceBatch.ShowTimeline, (c, v) => c.Config.DeviceBatch.ShowTimeline = v, "Show Timeline", "Can show device batch timeline", false) }, + { "Config.DeviceModel.ConfigureComponents", new Tuple, Action, string, string, bool>(c => c.Config.DeviceModel.ConfigureComponents, (c, v) => c.Config.DeviceModel.ConfigureComponents = v, "Configure Device Model Components", "Can configure device model components", false) }, + { "Config.DeviceModel.Configure", new Tuple, Action, string, string, bool>(c => c.Config.DeviceModel.Configure, (c, v) => c.Config.DeviceModel.Configure = v, "Configure Device Models", "Can configure device models", false) }, + { "Config.DeviceModel.Delete", new Tuple, Action, string, string, bool>(c => c.Config.DeviceModel.Delete, (c, v) => c.Config.DeviceModel.Delete = v, "Delete Device Models", "Can delete device models", false) }, + { "Config.DeviceModel.Show", new Tuple, Action, string, string, bool>(c => c.Config.DeviceModel.Show, (c, v) => c.Config.DeviceModel.Show = v, "Show Device Models", "Can show device models", false) }, + { "Config.DeviceProfile.ConfigureComputerNameTemplate", new Tuple, Action, string, string, bool>(c => c.Config.DeviceProfile.ConfigureComputerNameTemplate, (c, v) => c.Config.DeviceProfile.ConfigureComputerNameTemplate = v, "Configure Computer Name Templates", "Can configure computer name templates for device profiles", false) }, + { "Config.DeviceProfile.ConfigureDefaults", new Tuple, Action, string, string, bool>(c => c.Config.DeviceProfile.ConfigureDefaults, (c, v) => c.Config.DeviceProfile.ConfigureDefaults = v, "Configure Default Device Profiles", "Can configure default device profiles", false) }, + { "Config.DeviceProfile.Configure", new Tuple, Action, string, string, bool>(c => c.Config.DeviceProfile.Configure, (c, v) => c.Config.DeviceProfile.Configure = v, "Configure Device Profiles", "Can configure device profiles", false) }, + { "Config.DeviceProfile.Create", new Tuple, Action, string, string, bool>(c => c.Config.DeviceProfile.Create, (c, v) => c.Config.DeviceProfile.Create = v, "Create Device Profiles", "Can create device profiles", false) }, + { "Config.DeviceProfile.Delete", new Tuple, Action, string, string, bool>(c => c.Config.DeviceProfile.Delete, (c, v) => c.Config.DeviceProfile.Delete = v, "Delete Device Profiles", "Can delete device profiles", false) }, + { "Config.DeviceProfile.Show", new Tuple, Action, string, string, bool>(c => c.Config.DeviceProfile.Show, (c, v) => c.Config.DeviceProfile.Show = v, "Show Device Profiles", "Can show device profiles", false) }, + { "Config.DocumentTemplate.BulkGenerate", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.BulkGenerate, (c, v) => c.Config.DocumentTemplate.BulkGenerate = v, "Bulk Generate Document Templates", "Can bulk generate document templates", false) }, + { "Config.DocumentTemplate.Configure", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.Configure, (c, v) => c.Config.DocumentTemplate.Configure = v, "Configure Document Templates", "Can configure document templates", false) }, + { "Config.DocumentTemplate.ConfigureFilterExpression", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.ConfigureFilterExpression, (c, v) => c.Config.DocumentTemplate.ConfigureFilterExpression = v, "Configure Filter Expression", "Can configure filter expressions for document templates", false) }, + { "Config.DocumentTemplate.Create", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.Create, (c, v) => c.Config.DocumentTemplate.Create = v, "Create Document Templates", "Can create document templates", false) }, + { "Config.DocumentTemplate.Delete", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.Delete, (c, v) => c.Config.DocumentTemplate.Delete = v, "Delete Document Templates", "Can delete document templates", false) }, + { "Config.DocumentTemplate.ShowStatus", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.ShowStatus, (c, v) => c.Config.DocumentTemplate.ShowStatus = v, "Show Document Template Import Status", "Can show the document template import status", false) }, + { "Config.DocumentTemplate.Show", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.Show, (c, v) => c.Config.DocumentTemplate.Show = v, "Show Document Templates", "Can show document templates", false) }, + { "Config.DocumentTemplate.UndetectedPages", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.UndetectedPages, (c, v) => c.Config.DocumentTemplate.UndetectedPages = v, "Undetected Pages", "Can show and assign imported documents which were not undetected", false) }, + { "Config.DocumentTemplate.Upload", new Tuple, Action, string, string, bool>(c => c.Config.DocumentTemplate.Upload, (c, v) => c.Config.DocumentTemplate.Upload = v, "Upload Document Templates", "Can upload document templates", false) }, + { "Config.Logging.Show", new Tuple, Action, string, string, bool>(c => c.Config.Logging.Show, (c, v) => c.Config.Logging.Show = v, "Show Logging", "Can show logging", false) }, + { "Config.Plugin.Configure", new Tuple, Action, string, string, bool>(c => c.Config.Plugin.Configure, (c, v) => c.Config.Plugin.Configure = v, "Configure Plugins", "Can configure plugins", false) }, + { "Config.Plugin.InstallLocal", new Tuple, Action, string, string, bool>(c => c.Config.Plugin.InstallLocal, (c, v) => c.Config.Plugin.InstallLocal = v, "Install/Update Local Plugins", "Can install and update locally uploaded plugins", false) }, + { "Config.Plugin.Install", new Tuple, Action, string, string, bool>(c => c.Config.Plugin.Install, (c, v) => c.Config.Plugin.Install = v, "Install/Update Plugins", "Can install and update plugins", false) }, + { "Config.Plugin.Show", new Tuple, Action, string, string, bool>(c => c.Config.Plugin.Show, (c, v) => c.Config.Plugin.Show = v, "Show Plugins", "Can show plugins", false) }, + { "Config.Plugin.Uninstall", new Tuple, Action, string, string, bool>(c => c.Config.Plugin.Uninstall, (c, v) => c.Config.Plugin.Uninstall = v, "Uninstall Plugins", "Can uninstall plugins", false) }, + { "Config.System.ConfigureProxy", new Tuple, Action, string, string, bool>(c => c.Config.System.ConfigureProxy, (c, v) => c.Config.System.ConfigureProxy = v, "Configure Proxy Settings", "Can configure the proxy settings", false) }, + { "Config.System.Show", new Tuple, Action, string, string, bool>(c => c.Config.System.Show, (c, v) => c.Config.System.Show = v, "Show System Configuration", "Can show the system configuration", false) }, + { "Config.Organisation.ConfigureAddresses", new Tuple, Action, string, string, bool>(c => c.Config.Organisation.ConfigureAddresses, (c, v) => c.Config.Organisation.ConfigureAddresses = v, "Configure Addresses", "Can configure organisation addresses", false) }, + { "Config.Organisation.ConfigureLogo", new Tuple, Action, string, string, bool>(c => c.Config.Organisation.ConfigureLogo, (c, v) => c.Config.Organisation.ConfigureLogo = v, "Configure Logo", "Can configure the organisation logo", false) }, + { "Config.Organisation.ConfigureMultiSiteMode", new Tuple, Action, string, string, bool>(c => c.Config.Organisation.ConfigureMultiSiteMode, (c, v) => c.Config.Organisation.ConfigureMultiSiteMode = v, "Configure Multi-Site Mode", "Can configure multi-site mode", false) }, + { "Config.Organisation.ConfigureName", new Tuple, Action, string, string, bool>(c => c.Config.Organisation.ConfigureName, (c, v) => c.Config.Organisation.ConfigureName = v, "Configure Name", "Can configure the organisation name", false) }, + { "Config.Organisation.Show", new Tuple, Action, string, string, bool>(c => c.Config.Organisation.Show, (c, v) => c.Config.Organisation.Show = v, "Show Organisation Details", "Can show the organisation details", false) }, + { "Config.Show", new Tuple, Action, string, string, bool>(c => c.Config.Show, (c, v) => c.Config.Show = v, "Show Configuration", "Can show the configuration menu", false) }, + { "Job.Lists.AllOpen", new Tuple, Action, string, string, bool>(c => c.Job.Lists.AllOpen, (c, v) => c.Job.Lists.AllOpen = v, "All Open List", "Can show list", false) }, + { "Job.Lists.AwaitingFinanceAgreementBreach", new Tuple, Action, string, string, bool>(c => c.Job.Lists.AwaitingFinanceAgreementBreach, (c, v) => c.Job.Lists.AwaitingFinanceAgreementBreach = v, "Awaiting Finance Agreement Breach List", "Can show list (NOTE: Requires Awaiting Finance List)", false) }, + { "Job.Lists.AwaitingFinanceCharge", new Tuple, Action, string, string, bool>(c => c.Job.Lists.AwaitingFinanceCharge, (c, v) => c.Job.Lists.AwaitingFinanceCharge = v, "Awaiting Finance Charge List", "Can show list (NOTE: Requires Awaiting Finance List)", false) }, + { "Job.Lists.AwaitingFinanceInsuranceProcessing", new Tuple, Action, string, string, bool>(c => c.Job.Lists.AwaitingFinanceInsuranceProcessing, (c, v) => c.Job.Lists.AwaitingFinanceInsuranceProcessing = v, "Awaiting Finance Insurance Processing List", "Can show list (NOTE: Requires Awaiting Finance List)", false) }, + { "Job.Lists.AwaitingFinance", new Tuple, Action, string, string, bool>(c => c.Job.Lists.AwaitingFinance, (c, v) => c.Job.Lists.AwaitingFinance = v, "Awaiting Finance List", "Can show list", false) }, + { "Job.Lists.AwaitingFinancePayment", new Tuple, Action, string, string, bool>(c => c.Job.Lists.AwaitingFinancePayment, (c, v) => c.Job.Lists.AwaitingFinancePayment = v, "Awaiting Finance Payment List", "Can show list (NOTE: Requires Awaiting Finance List)", false) }, + { "Job.Lists.AwaitingTechnicianAction", new Tuple, Action, string, string, bool>(c => c.Job.Lists.AwaitingTechnicianAction, (c, v) => c.Job.Lists.AwaitingTechnicianAction = v, "Awaiting Technician Action List", "Can show list", false) }, + { "Job.Lists.AwaitingUserAction", new Tuple, Action, string, string, bool>(c => c.Job.Lists.AwaitingUserAction, (c, v) => c.Job.Lists.AwaitingUserAction = v, "Awaiting User Action List", "Can show list", false) }, + { "Job.Lists.DevicesAwaitingRepair", new Tuple, Action, string, string, bool>(c => c.Job.Lists.DevicesAwaitingRepair, (c, v) => c.Job.Lists.DevicesAwaitingRepair = v, "Devices Awaiting Repair List", "Can show list", false) }, + { "Job.Lists.DevicesReadyForReturn", new Tuple, Action, string, string, bool>(c => c.Job.Lists.DevicesReadyForReturn, (c, v) => c.Job.Lists.DevicesReadyForReturn = v, "Devices Ready For Return List", "Can show list", false) }, + { "Job.Lists.Locations", new Tuple, Action, string, string, bool>(c => c.Job.Lists.Locations, (c, v) => c.Job.Lists.Locations = v, "Locations List", "Can show list", false) }, + { "Job.Lists.LongRunningJobs", new Tuple, Action, string, string, bool>(c => c.Job.Lists.LongRunningJobs, (c, v) => c.Job.Lists.LongRunningJobs = v, "Long Running Jobs List", "Can show list", false) }, + { "Job.Lists.RecentlyClosed", new Tuple, Action, string, string, bool>(c => c.Job.Lists.RecentlyClosed, (c, v) => c.Job.Lists.RecentlyClosed = v, "Recently Closed List", "Can show list", false) }, + { "Job.Actions.AddAttachments", new Tuple, Action, string, string, bool>(c => c.Job.Actions.AddAttachments, (c, v) => c.Job.Actions.AddAttachments = v, "Add Attachments", "Can add attachments to jobs", false) }, + { "Job.Actions.AddLogs", new Tuple, Action, string, string, bool>(c => c.Job.Actions.AddLogs, (c, v) => c.Job.Actions.AddLogs = v, "Add Logs", "Can add job logs", false) }, + { "Job.Actions.Close", new Tuple, Action, string, string, bool>(c => c.Job.Actions.Close, (c, v) => c.Job.Actions.Close = v, "Close Jobs", "Can close jobs", false) }, + { "Job.Actions.ConvertHWarToHNWar", new Tuple, Action, string, string, bool>(c => c.Job.Actions.ConvertHWarToHNWar, (c, v) => c.Job.Actions.ConvertHWarToHNWar = v, "Convert HWar Jobs To HNWar", "Can convert warranty jobs to non-warranty jobs", false) }, + { "Job.Actions.Create", new Tuple, Action, string, string, bool>(c => c.Job.Actions.Create, (c, v) => c.Job.Actions.Create = v, "Create Jobs", "Can create jobs", false) }, + { "Job.Actions.Delete", new Tuple, Action, string, string, bool>(c => c.Job.Actions.Delete, (c, v) => c.Job.Actions.Delete = v, "Delete Jobs", "Can delete jobs", false) }, + { "Job.Actions.ForceClose", new Tuple, Action, string, string, bool>(c => c.Job.Actions.ForceClose, (c, v) => c.Job.Actions.ForceClose = v, "Force Close Jobs", "Can force close jobs", false) }, + { "Job.Actions.GenerateDocuments", new Tuple, Action, string, string, bool>(c => c.Job.Actions.GenerateDocuments, (c, v) => c.Job.Actions.GenerateDocuments = v, "Generate Documents", "Can generate documents for jobs", false) }, + { "Job.Actions.LogRepair", new Tuple, Action, string, string, bool>(c => c.Job.Actions.LogRepair, (c, v) => c.Job.Actions.LogRepair = v, "Log Repair", "Can log repair for non-warranty jobs", false) }, + { "Job.Actions.LogWarranty", new Tuple, Action, string, string, bool>(c => c.Job.Actions.LogWarranty, (c, v) => c.Job.Actions.LogWarranty = v, "Log Warranty", "Can log warranty for jobs", false) }, + { "Job.Actions.RemoveAnyAttachments", new Tuple, Action, string, string, bool>(c => c.Job.Actions.RemoveAnyAttachments, (c, v) => c.Job.Actions.RemoveAnyAttachments = v, "Remove Any Attachments", "Can remove any attachments from jobs", false) }, + { "Job.Actions.RemoveAnyLogs", new Tuple, Action, string, string, bool>(c => c.Job.Actions.RemoveAnyLogs, (c, v) => c.Job.Actions.RemoveAnyLogs = v, "Remove Any Logs", "Can remove any job logs", false) }, + { "Job.Actions.RemoveOwnAttachments", new Tuple, Action, string, string, bool>(c => c.Job.Actions.RemoveOwnAttachments, (c, v) => c.Job.Actions.RemoveOwnAttachments = v, "Remove Own Attachments", "Can remove own attachments from jobs", false) }, + { "Job.Actions.RemoveOwnLogs", new Tuple, Action, string, string, bool>(c => c.Job.Actions.RemoveOwnLogs, (c, v) => c.Job.Actions.RemoveOwnLogs = v, "Remove Own Logs", "Can remove own job logs", false) }, + { "Job.Actions.Reopen", new Tuple, Action, string, string, bool>(c => c.Job.Actions.Reopen, (c, v) => c.Job.Actions.Reopen = v, "Reopen Jobs", "Can reopen jobs", false) }, + { "Job.Actions.UpdateSubTypes", new Tuple, Action, string, string, bool>(c => c.Job.Actions.UpdateSubTypes, (c, v) => c.Job.Actions.UpdateSubTypes = v, "Update Sub Types", "Can update sub types for jobs", false) }, + { "Job.Properties.WarrantyProperties.ExternalCompletedDate", new Tuple, Action, string, string, bool>(c => c.Job.Properties.WarrantyProperties.ExternalCompletedDate, (c, v) => c.Job.Properties.WarrantyProperties.ExternalCompletedDate = v, "External Completed Date Property", "Can update property", false) }, + { "Job.Properties.WarrantyProperties.ExternalLoggedDate", new Tuple, Action, string, string, bool>(c => c.Job.Properties.WarrantyProperties.ExternalLoggedDate, (c, v) => c.Job.Properties.WarrantyProperties.ExternalLoggedDate = v, "External Logged Date Property", "Can update property", false) }, + { "Job.Properties.WarrantyProperties.ExternalName", new Tuple, Action, string, string, bool>(c => c.Job.Properties.WarrantyProperties.ExternalName, (c, v) => c.Job.Properties.WarrantyProperties.ExternalName = v, "External Name Property", "Can update property", false) }, + { "Job.Properties.WarrantyProperties.ExternalReference", new Tuple, Action, string, string, bool>(c => c.Job.Properties.WarrantyProperties.ExternalReference, (c, v) => c.Job.Properties.WarrantyProperties.ExternalReference = v, "External Reference Property", "Can update property", false) }, + { "Job.Properties.WarrantyProperties.ProviderDetails", new Tuple, Action, string, string, bool>(c => c.Job.Properties.WarrantyProperties.ProviderDetails, (c, v) => c.Job.Properties.WarrantyProperties.ProviderDetails = v, "Provider Details", "Can access warranty provider details", false) }, + { "Job.Properties.WarrantyProperties.WarrantyCompleted", new Tuple, Action, string, string, bool>(c => c.Job.Properties.WarrantyProperties.WarrantyCompleted, (c, v) => c.Job.Properties.WarrantyProperties.WarrantyCompleted = v, "Warranty Completed Property", "Can update property", false) }, + { "Job.Properties.NonWarrantyProperties.AccountingChargeAdded", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.AccountingChargeAdded, (c, v) => c.Job.Properties.NonWarrantyProperties.AccountingChargeAdded = v, "Accounting Charge Added Property", "Can update property", false) }, + { "Job.Properties.NonWarrantyProperties.AccountingChargePaid", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.AccountingChargePaid, (c, v) => c.Job.Properties.NonWarrantyProperties.AccountingChargePaid = v, "Accounting Charge Paid Property", "Can update property", false) }, + { "Job.Properties.NonWarrantyProperties.AccountingChargeRequired", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.AccountingChargeRequired, (c, v) => c.Job.Properties.NonWarrantyProperties.AccountingChargeRequired = v, "Accounting Charge Required Property", "Can update property", false) }, + { "Job.Properties.NonWarrantyProperties.AddComponents", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.AddComponents, (c, v) => c.Job.Properties.NonWarrantyProperties.AddComponents = v, "Add Components", "Can add job components (NOTE: Requires Edit Components)", false) }, + { "Job.Properties.NonWarrantyProperties.EditComponents", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.EditComponents, (c, v) => c.Job.Properties.NonWarrantyProperties.EditComponents = v, "Edit Components", "Can edit and remove job components", false) }, + { "Job.Properties.NonWarrantyProperties.InsuranceClaimFormSent", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.InsuranceClaimFormSent, (c, v) => c.Job.Properties.NonWarrantyProperties.InsuranceClaimFormSent = v, "Insurance Claim Form Sent Property", "Can update property", false) }, + { "Job.Properties.NonWarrantyProperties.InsuranceDetails", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.InsuranceDetails, (c, v) => c.Job.Properties.NonWarrantyProperties.InsuranceDetails = v, "Insurance Detail Properties", "Can update insurance detail properties", false) }, + { "Job.Properties.NonWarrantyProperties.InvoiceReceived", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.InvoiceReceived, (c, v) => c.Job.Properties.NonWarrantyProperties.InvoiceReceived = v, "Invoice Received Property", "Can update property", false) }, + { "Job.Properties.NonWarrantyProperties.IsInsuranceClaim", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.IsInsuranceClaim, (c, v) => c.Job.Properties.NonWarrantyProperties.IsInsuranceClaim = v, "Is Insurance Claim Property", "Can update property", false) }, + { "Job.Properties.NonWarrantyProperties.PurchaseOrderRaised", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.PurchaseOrderRaised, (c, v) => c.Job.Properties.NonWarrantyProperties.PurchaseOrderRaised = v, "Purchase Order Raised Property", "Can update property", false) }, + { "Job.Properties.NonWarrantyProperties.PurchaseOrderReference", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.PurchaseOrderReference, (c, v) => c.Job.Properties.NonWarrantyProperties.PurchaseOrderReference = v, "Purchase Order Reference Property", "Can update property", false) }, + { "Job.Properties.NonWarrantyProperties.PurchaseOrderSent", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.PurchaseOrderSent, (c, v) => c.Job.Properties.NonWarrantyProperties.PurchaseOrderSent = v, "Purchase Order Sent Property", "Can update property", false) }, + { "Job.Properties.NonWarrantyProperties.RepairerCompletedDate", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.RepairerCompletedDate, (c, v) => c.Job.Properties.NonWarrantyProperties.RepairerCompletedDate = v, "Repairer Completed Date Property", "Can update property", false) }, + { "Job.Properties.NonWarrantyProperties.RepairerLoggedDate", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.RepairerLoggedDate, (c, v) => c.Job.Properties.NonWarrantyProperties.RepairerLoggedDate = v, "Repairer Logged Date Property", "Can update property", false) }, + { "Job.Properties.NonWarrantyProperties.RepairerName", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.RepairerName, (c, v) => c.Job.Properties.NonWarrantyProperties.RepairerName = v, "Repairer Name Property", "Can update property", false) }, + { "Job.Properties.NonWarrantyProperties.RepairerReference", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NonWarrantyProperties.RepairerReference, (c, v) => c.Job.Properties.NonWarrantyProperties.RepairerReference = v, "Repairer Reference Property", "Can update property", false) }, + { "Job.Properties.DeviceHeldLocation", new Tuple, Action, string, string, bool>(c => c.Job.Properties.DeviceHeldLocation, (c, v) => c.Job.Properties.DeviceHeldLocation = v, "Device Held Location Property", "Can update property", false) }, + { "Job.Properties.DeviceHeld", new Tuple, Action, string, string, bool>(c => c.Job.Properties.DeviceHeld, (c, v) => c.Job.Properties.DeviceHeld = v, "Device Held Property", "Can update property", false) }, + { "Job.Properties.DeviceReadyForReturn", new Tuple, Action, string, string, bool>(c => c.Job.Properties.DeviceReadyForReturn, (c, v) => c.Job.Properties.DeviceReadyForReturn = v, "Device Ready For Return Property", "Can update property", false) }, + { "Job.Properties.DeviceReturned", new Tuple, Action, string, string, bool>(c => c.Job.Properties.DeviceReturned, (c, v) => c.Job.Properties.DeviceReturned = v, "Device Returned Property", "Can update property", false) }, + { "Job.Properties.ExpectedClosedDate", new Tuple, Action, string, string, bool>(c => c.Job.Properties.ExpectedClosedDate, (c, v) => c.Job.Properties.ExpectedClosedDate = v, "Expected Closed Date Property", "Can update property", false) }, + { "Job.Properties.Flags", new Tuple, Action, string, string, bool>(c => c.Job.Properties.Flags, (c, v) => c.Job.Properties.Flags = v, "Flags Property", "Can update property", false) }, + { "Job.Properties.NotWaitingForUserAction", new Tuple, Action, string, string, bool>(c => c.Job.Properties.NotWaitingForUserAction, (c, v) => c.Job.Properties.NotWaitingForUserAction = v, "Not Waiting For User Action Property", "Can update property", false) }, + { "Job.Properties.WaitingForUserAction", new Tuple, Action, string, string, bool>(c => c.Job.Properties.WaitingForUserAction, (c, v) => c.Job.Properties.WaitingForUserAction = v, "Waiting For User Action Property", "Can update property", false) }, + { "Job.Types.ShowHMisc", new Tuple, Action, string, string, bool>(c => c.Job.Types.ShowHMisc, (c, v) => c.Job.Types.ShowHMisc = v, "Show Hardware - Miscellaneous Jobs", "Can show jobs of this type", false) }, + { "Job.Types.ShowHNWar", new Tuple, Action, string, string, bool>(c => c.Job.Types.ShowHNWar, (c, v) => c.Job.Types.ShowHNWar = v, "Show Hardware - Non-Warranty Jobs", "Can show jobs of this type", false) }, + { "Job.Types.ShowHWar", new Tuple, Action, string, string, bool>(c => c.Job.Types.ShowHWar, (c, v) => c.Job.Types.ShowHWar = v, "Show Hardware - Warranty Jobs", "Can show jobs of this type", false) }, + { "Job.Types.ShowSApp", new Tuple, Action, string, string, bool>(c => c.Job.Types.ShowSApp, (c, v) => c.Job.Types.ShowSApp = v, "Show Software - Application Jobs", "Can show jobs of this type", false) }, + { "Job.Types.ShowSOS", new Tuple, Action, string, string, bool>(c => c.Job.Types.ShowSOS, (c, v) => c.Job.Types.ShowSOS = v, "Show Software - Operating System Jobs", "Can show jobs of this type", false) }, + { "Job.Types.ShowSImg", new Tuple, Action, string, string, bool>(c => c.Job.Types.ShowSImg, (c, v) => c.Job.Types.ShowSImg = v, "Show Software - Reimage Jobs", "Can show jobs of this type", false) }, + { "Job.Types.ShowUMgmt", new Tuple, Action, string, string, bool>(c => c.Job.Types.ShowUMgmt, (c, v) => c.Job.Types.ShowUMgmt = v, "Show User Management Jobs", "Can show jobs of this type", false) }, + { "Job.Search", new Tuple, Action, string, string, bool>(c => c.Job.Search, (c, v) => c.Job.Search = v, "Search Jobs", "Can search jobs", false) }, + { "Job.ShowAttachments", new Tuple, Action, string, string, bool>(c => c.Job.ShowAttachments, (c, v) => c.Job.ShowAttachments = v, "Show Attachments", "Can show job attachments", false) }, + { "Job.ShowDailyChart", new Tuple, Action, string, string, bool>(c => c.Job.ShowDailyChart, (c, v) => c.Job.ShowDailyChart = v, "Show Daily Opened & Closed", "Can show daily opened & closed chart", false) }, + { "Job.ShowFlags", new Tuple, Action, string, string, bool>(c => c.Job.ShowFlags, (c, v) => c.Job.ShowFlags = v, "Show Flags", "Can show job flags", false) }, + { "Job.Show", new Tuple, Action, string, string, bool>(c => c.Job.Show, (c, v) => c.Job.Show = v, "Show Jobs", "Can show jobs", false) }, + { "Job.ShowLogs", new Tuple, Action, string, string, bool>(c => c.Job.ShowLogs, (c, v) => c.Job.ShowLogs = v, "Show Logs", "Can show job logs", false) }, + { "Job.ShowNonWarrantyComponents", new Tuple, Action, string, string, bool>(c => c.Job.ShowNonWarrantyComponents, (c, v) => c.Job.ShowNonWarrantyComponents = v, "Show Non-Warranty Components", "Can show non-warranty job components", false) }, + { "Job.ShowNonWarrantyFinance", new Tuple, Action, string, string, bool>(c => c.Job.ShowNonWarrantyFinance, (c, v) => c.Job.ShowNonWarrantyFinance = v, "Show Non-Warranty Finance", "Can show non-warranty job finance", false) }, + { "Job.ShowNonWarrantyInsurance", new Tuple, Action, string, string, bool>(c => c.Job.ShowNonWarrantyInsurance, (c, v) => c.Job.ShowNonWarrantyInsurance = v, "Show Non-Warranty Insurance", "Can show non-warranty job insurance", false) }, + { "Job.ShowNonWarrantyRepairs", new Tuple, Action, string, string, bool>(c => c.Job.ShowNonWarrantyRepairs, (c, v) => c.Job.ShowNonWarrantyRepairs = v, "Show Non-Warranty Repairs", "Can show non-warranty job repairs", false) }, + { "Job.ShowWarranty", new Tuple, Action, string, string, bool>(c => c.Job.ShowWarranty, (c, v) => c.Job.ShowWarranty = v, "Show Warranty", "Can show job warranty", false) }, + { "Device.Properties.AssetNumber", new Tuple, Action, string, string, bool>(c => c.Device.Properties.AssetNumber, (c, v) => c.Device.Properties.AssetNumber = v, "Asset Number Property", "Can update property", false) }, + { "Device.Properties.DeviceBatch", new Tuple, Action, string, string, bool>(c => c.Device.Properties.DeviceBatch, (c, v) => c.Device.Properties.DeviceBatch = v, "Device Batch Property", "Can update property", false) }, + { "Device.Properties.DeviceProfile", new Tuple, Action, string, string, bool>(c => c.Device.Properties.DeviceProfile, (c, v) => c.Device.Properties.DeviceProfile = v, "Device Profile Property", "Can update property", false) }, + { "Device.Properties.Location", new Tuple, Action, string, string, bool>(c => c.Device.Properties.Location, (c, v) => c.Device.Properties.Location = v, "Location Property", "Can update property", false) }, + { "Device.Actions.AddAttachments", new Tuple, Action, string, string, bool>(c => c.Device.Actions.AddAttachments, (c, v) => c.Device.Actions.AddAttachments = v, "Add Attachments", "Can add attachments to devices", false) }, + { "Device.Actions.AllowUnauthenticatedEnrol", new Tuple, Action, string, string, bool>(c => c.Device.Actions.AllowUnauthenticatedEnrol, (c, v) => c.Device.Actions.AllowUnauthenticatedEnrol = v, "Allow Unauthenticated Enrol", "Can allow devices to enrol without authentication", false) }, + { "Device.Actions.AssignUser", new Tuple, Action, string, string, bool>(c => c.Device.Actions.AssignUser, (c, v) => c.Device.Actions.AssignUser = v, "Assign User", "Can update the user assignment of devices", false) }, + { "Device.Actions.Decommission", new Tuple, Action, string, string, bool>(c => c.Device.Actions.Decommission, (c, v) => c.Device.Actions.Decommission = v, "Decommission", "Can decommission devices", false) }, + { "Device.Actions.Delete", new Tuple, Action, string, string, bool>(c => c.Device.Actions.Delete, (c, v) => c.Device.Actions.Delete = v, "Delete", "Can delete devices", false) }, + { "Device.Actions.EnrolDevices", new Tuple, Action, string, string, bool>(c => c.Device.Actions.EnrolDevices, (c, v) => c.Device.Actions.EnrolDevices = v, "Enrol Devices", "Can add devices offline and enrol devices with the Bootstrapper", false) }, + { "Device.Actions.Export", new Tuple, Action, string, string, bool>(c => c.Device.Actions.Export, (c, v) => c.Device.Actions.Export = v, "Export Devices", "Can export devices in a bulk format", false) }, + { "Device.Actions.GenerateDocuments", new Tuple, Action, string, string, bool>(c => c.Device.Actions.GenerateDocuments, (c, v) => c.Device.Actions.GenerateDocuments = v, "Generate Documents", "Can generate documents for jobs", false) }, + { "Device.Actions.Import", new Tuple, Action, string, string, bool>(c => c.Device.Actions.Import, (c, v) => c.Device.Actions.Import = v, "Import Devices", "Can bulk import devices", false) }, + { "Device.Actions.Recommission", new Tuple, Action, string, string, bool>(c => c.Device.Actions.Recommission, (c, v) => c.Device.Actions.Recommission = v, "Recommission", "Can recommission devices", false) }, + { "Device.Actions.RemoveAnyAttachments", new Tuple, Action, string, string, bool>(c => c.Device.Actions.RemoveAnyAttachments, (c, v) => c.Device.Actions.RemoveAnyAttachments = v, "Remove Any Attachments", "Can remove any attachments from devices", false) }, + { "Device.Actions.RemoveOwnAttachments", new Tuple, Action, string, string, bool>(c => c.Device.Actions.RemoveOwnAttachments, (c, v) => c.Device.Actions.RemoveOwnAttachments = v, "Remove Own Attachments", "Can remove own attachments from devices", false) }, + { "Device.Search", new Tuple, Action, string, string, bool>(c => c.Device.Search, (c, v) => c.Device.Search = v, "Search Devices", "Can search devices", false) }, + { "Device.ShowAssignmentHistory", new Tuple, Action, string, string, bool>(c => c.Device.ShowAssignmentHistory, (c, v) => c.Device.ShowAssignmentHistory = v, "Show Assignment History", "Can show the assignment history for devices", false) }, + { "Device.ShowAttachments", new Tuple, Action, string, string, bool>(c => c.Device.ShowAttachments, (c, v) => c.Device.ShowAttachments = v, "Show Attachments", "Can show device attachments", false) }, + { "Device.ShowCertificates", new Tuple, Action, string, string, bool>(c => c.Device.ShowCertificates, (c, v) => c.Device.ShowCertificates = v, "Show Certificates", "Can show certificates associated with devices", false) }, + { "Device.ShowDetails", new Tuple, Action, string, string, bool>(c => c.Device.ShowDetails, (c, v) => c.Device.ShowDetails = v, "Show Details", "Can show details associated with devices", false) }, + { "Device.Show", new Tuple, Action, string, string, bool>(c => c.Device.Show, (c, v) => c.Device.Show = v, "Show Devices", "Can show devices", false) }, + { "Device.ShowJobs", new Tuple, Action, string, string, bool>(c => c.Device.ShowJobs, (c, v) => c.Device.ShowJobs = v, "Show Devices Jobs", "Can show jobs associated with devices", false) }, + { "User.Actions.AddAttachments", new Tuple, Action, string, string, bool>(c => c.User.Actions.AddAttachments, (c, v) => c.User.Actions.AddAttachments = v, "Add Attachments", "Can add attachments to users", false) }, + { "User.Actions.GenerateDocuments", new Tuple, Action, string, string, bool>(c => c.User.Actions.GenerateDocuments, (c, v) => c.User.Actions.GenerateDocuments = v, "Generate Documents", "Can generate documents for users", false) }, + { "User.Actions.RemoveAnyAttachments", new Tuple, Action, string, string, bool>(c => c.User.Actions.RemoveAnyAttachments, (c, v) => c.User.Actions.RemoveAnyAttachments = v, "Remove Any Attachments", "Can remove any attachments from users", false) }, + { "User.Actions.RemoveOwnAttachments", new Tuple, Action, string, string, bool>(c => c.User.Actions.RemoveOwnAttachments, (c, v) => c.User.Actions.RemoveOwnAttachments = v, "Remove Own Attachments", "Can remove own attachments from users", false) }, + { "User.Search", new Tuple, Action, string, string, bool>(c => c.User.Search, (c, v) => c.User.Search = v, "Search Users", "Can search users", false) }, + { "User.ShowAttachments", new Tuple, Action, string, string, bool>(c => c.User.ShowAttachments, (c, v) => c.User.ShowAttachments = v, "Show Attachments", "Can show user attachments", false) }, + { "User.ShowAssignmentHistory", new Tuple, Action, string, string, bool>(c => c.User.ShowAssignmentHistory, (c, v) => c.User.ShowAssignmentHistory = v, "Show Device Assignment History", "Can show the device assignment history for users", false) }, + { "User.Show", new Tuple, Action, string, string, bool>(c => c.User.Show, (c, v) => c.User.Show = v, "Show Users", "Can show users", false) }, + { "User.ShowAuthorization", new Tuple, Action, string, string, bool>(c => c.User.ShowAuthorization, (c, v) => c.User.ShowAuthorization = v, "Show Users Authorization", "Can show authorization permissions associated with users", false) }, + { "User.ShowJobs", new Tuple, Action, string, string, bool>(c => c.User.ShowJobs, (c, v) => c.User.ShowJobs = v, "Show Users Jobs", "Can show jobs associated with users", false) }, + { "ComputerAccount", new Tuple, Action, string, string, bool>(c => c.ComputerAccount, (c, v) => c.ComputerAccount = v, "Computer Account", "Represents a computer account", true) }, + { "DiscoAdminAccount", new Tuple, Action, string, string, bool>(c => c.DiscoAdminAccount, (c, v) => c.DiscoAdminAccount = v, "Disco Administrator Account", "Represents a Disco Administrator account", true) } }; #endregion @@ -394,31 +395,40 @@ namespace Disco.Services.Authorization get { return _claimNavigator; } } - public static Func GetClaimAccessor(string ClaimKey) { - Tuple, Action, string, string> claimFunc; + internal static Tuple, Action, string, string, bool> GetClaimDefinition(string ClaimKey) { + Tuple, Action, string, string, bool> claimDef; - if (!_roleClaims.TryGetValue(ClaimKey, out claimFunc)) + if (!_roleClaims.TryGetValue(ClaimKey, out claimDef)) throw new ArgumentException("Unknown Claim Key", "ClaimKey"); - return claimFunc.Item1; + return new Tuple, Action, string, string, bool>(claimDef.Item1, claimDef.Item2, claimDef.Item3, claimDef.Item4, claimDef.Item5); + } + + public static Func GetClaimAccessor(string ClaimKey) { + Tuple, Action, string, string, bool> claimDef; + + if (!_roleClaims.TryGetValue(ClaimKey, out claimDef)) + throw new ArgumentException("Unknown Claim Key", "ClaimKey"); + + return claimDef.Item1; } public static Action GetClaimSetter(string ClaimKey) { - Tuple, Action, string, string> claimFunc; + Tuple, Action, string, string, bool> claimDef; - if (!_roleClaims.TryGetValue(ClaimKey, out claimFunc)) + if (!_roleClaims.TryGetValue(ClaimKey, out claimDef)) throw new ArgumentException("Unknown Claim Key", "ClaimKey"); - return claimFunc.Item2; + return claimDef.Item2; } - public static Tuple GetClaimDetails(string ClaimKey) { - Tuple, Action, string, string> claimFunc; + public static Tuple GetClaimDetails(string ClaimKey) { + Tuple, Action, string, string, bool> claimDef; - if (!_roleClaims.TryGetValue(ClaimKey, out claimFunc)) + if (!_roleClaims.TryGetValue(ClaimKey, out claimDef)) throw new ArgumentException("Unknown Claim Key", "ClaimKey"); - return new Tuple(claimFunc.Item3, claimFunc.Item4); + return new Tuple(claimDef.Item3, claimDef.Item4, claimDef.Item5); } public static RoleClaims BuildClaims(IEnumerable ClaimKeys){ @@ -429,6 +439,12 @@ namespace Disco.Services.Authorization return c; } + public static List GetClaimKeys(RoleClaims Claims) + { + var claims = Claims; + return _roleClaims.Where(rc => rc.Value.Item1(claims)).Select(rc => rc.Key).ToList(); + } + public static RoleClaims AdministratorClaims() { var c = new RoleClaims(); #region Set All Administrator Claims @@ -1546,9 +1562,10 @@ namespace Disco.Services.Authorization public static void Set(this RoleClaims c, string ClaimKey, bool Value) { - Action claimSetter = Claims.GetClaimSetter(ClaimKey); + var claimDefinition = Claims.GetClaimDefinition(ClaimKey); - claimSetter(c, Value); + if (!claimDefinition.Item5) + claimDefinition.Item2(c, Value); } public static void SetClaims(this AuthorizationRole role, RoleClaims Claims) diff --git a/Disco.Services/Authorization/Claims.tt b/Disco.Services/Authorization/Claims.tt index 5e1632b3..cb4f5ebc 100644 --- a/Disco.Services/Authorization/Claims.tt +++ b/Disco.Services/Authorization/Claims.tt @@ -63,13 +63,13 @@ namespace Disco.Services.Authorization { public static class Claims { - private static Dictionary, Action, string, string>> _roleClaims; + private static Dictionary, Action, string, string, bool>> _roleClaims; private static ClaimNavigatorItem _claimNavigator; static Claims() { #region Role Claim Dictionary - _roleClaims = new Dictionary, Action, string, string>>() + _roleClaims = new Dictionary, Action, string, string, bool>>() { <#WriteAccessHashes(permissionRoot);#> }; @@ -86,31 +86,40 @@ namespace Disco.Services.Authorization get { return _claimNavigator; } } - public static Func GetClaimAccessor(string ClaimKey) { - Tuple, Action, string, string> claimFunc; + internal static Tuple, Action, string, string, bool> GetClaimDefinition(string ClaimKey) { + Tuple, Action, string, string, bool> claimDef; - if (!_roleClaims.TryGetValue(ClaimKey, out claimFunc)) + if (!_roleClaims.TryGetValue(ClaimKey, out claimDef)) throw new ArgumentException("Unknown Claim Key", "ClaimKey"); - return claimFunc.Item1; + return new Tuple, Action, string, string, bool>(claimDef.Item1, claimDef.Item2, claimDef.Item3, claimDef.Item4, claimDef.Item5); + } + + public static Func GetClaimAccessor(string ClaimKey) { + Tuple, Action, string, string, bool> claimDef; + + if (!_roleClaims.TryGetValue(ClaimKey, out claimDef)) + throw new ArgumentException("Unknown Claim Key", "ClaimKey"); + + return claimDef.Item1; } public static Action GetClaimSetter(string ClaimKey) { - Tuple, Action, string, string> claimFunc; + Tuple, Action, string, string, bool> claimDef; - if (!_roleClaims.TryGetValue(ClaimKey, out claimFunc)) + if (!_roleClaims.TryGetValue(ClaimKey, out claimDef)) throw new ArgumentException("Unknown Claim Key", "ClaimKey"); - return claimFunc.Item2; + return claimDef.Item2; } - public static Tuple GetClaimDetails(string ClaimKey) { - Tuple, Action, string, string> claimFunc; + public static Tuple GetClaimDetails(string ClaimKey) { + Tuple, Action, string, string, bool> claimDef; - if (!_roleClaims.TryGetValue(ClaimKey, out claimFunc)) + if (!_roleClaims.TryGetValue(ClaimKey, out claimDef)) throw new ArgumentException("Unknown Claim Key", "ClaimKey"); - return new Tuple(claimFunc.Item3, claimFunc.Item4); + return new Tuple(claimDef.Item3, claimDef.Item4, claimDef.Item5); } public static RoleClaims BuildClaims(IEnumerable ClaimKeys){ @@ -150,9 +159,10 @@ namespace Disco.Services.Authorization public static void Set(this RoleClaims c, string ClaimKey, bool Value) { - Action claimSetter = Claims.GetClaimSetter(ClaimKey); + var claimDefinition = Claims.GetClaimDefinition(ClaimKey); - claimSetter(c, Value); + if (!claimDefinition.Item5) + claimDefinition.Item2(c, Value); } public static void SetClaims(this AuthorizationRole role, RoleClaims Claims) @@ -250,7 +260,7 @@ void WriteAccessHashes_Recurse(Permission p, string Prefix, Stack parent parents.Pop(); }else{ var fqn = string.Concat(Prefix, p.Name); - hashes.AppendFormat(" {{ \"{0}\", new Tuple, Action, string, string>(c => {1}.{2}, (c, v) => {1}.{2} = v, \"{3}\", \"{4}\") }},", fqn, parents.Peek(), p.Name, p.FriendlyName, p.Description); + hashes.AppendFormat(" {{ \"{0}\", new Tuple, Action, string, string, bool>(c => {1}.{2}, (c, v) => {1}.{2} = v, \"{3}\", \"{4}\", {5}) }},", fqn, parents.Peek(), p.Name, p.FriendlyName, p.Description, p.Hidden ? "true" : "false"); hashes.AppendLine(); } } diff --git a/Disco.Services/Authorization/DiscoAuthorizeBaseAttribute.cs b/Disco.Services/Authorization/DiscoAuthorizeBaseAttribute.cs index ccb1c27e..bad4e0d7 100644 --- a/Disco.Services/Authorization/DiscoAuthorizeBaseAttribute.cs +++ b/Disco.Services/Authorization/DiscoAuthorizeBaseAttribute.cs @@ -10,6 +10,8 @@ namespace Disco.Services.Authorization { public abstract class DiscoAuthorizeBaseAttribute : AuthorizeAttribute { + public string AuthorizeResource { get; set; } + protected AuthorizationToken Token { get @@ -33,7 +35,29 @@ namespace Disco.Services.Authorization { string resultMessage = HandleUnauthorizedMessage(); + LogAccessDenied(filterContext, resultMessage); + filterContext.Result = new HttpUnauthorizedResult(resultMessage); } + + public void LogAccessDenied(AuthorizationContext FilterContext, string ResultMessage) + { + // Don't log anonymous + if (Token != null) + { + // Calculate Authorize Resource + if (AuthorizeResource == null) + { + var controllerName = FilterContext.ActionDescriptor.ControllerDescriptor.ControllerName; + var actionName = FilterContext.ActionDescriptor.ActionName; + + AuthorizeResource = string.Format("{0}::{1}", controllerName, actionName); + } + + var resource = string.Format("{0} [{1}]", AuthorizeResource, FilterContext.HttpContext.Request.RawUrl); + + AuthorizationLog.LogAccessDenied(Token.User.Id, resource, ResultMessage); + } + } } } diff --git a/Disco.Services/Authorization/Roles/RoleCache.cs b/Disco.Services/Authorization/Roles/RoleCache.cs index 28497f99..6ffb47f8 100644 --- a/Disco.Services/Authorization/Roles/RoleCache.cs +++ b/Disco.Services/Authorization/Roles/RoleCache.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace Disco.Services.Authorization.Roles { - public static class RoleCache + internal static class RoleCache { internal const int AdministratorsTokenId = -1; internal const int ComputerAccountTokenId = -200; diff --git a/Disco.Services/Disco.Services.csproj b/Disco.Services/Disco.Services.csproj index 92b6ec3d..10a4582f 100644 --- a/Disco.Services/Disco.Services.csproj +++ b/Disco.Services/Disco.Services.csproj @@ -90,6 +90,7 @@ + @@ -223,7 +224,7 @@ - + diff --git a/Disco.Services/Plugins/PluginManifest.cs b/Disco.Services/Plugins/PluginManifest.cs index 517ad7be..e4c54eac 100644 --- a/Disco.Services/Plugins/PluginManifest.cs +++ b/Disco.Services/Plugins/PluginManifest.cs @@ -439,7 +439,7 @@ namespace Disco.Services.Plugins { var attributeDenied = this.WebHandlerAuthorizers.FirstOrDefault(a => !a.IsAuthorized(HostController.HttpContext)); if (attributeDenied != null) - throw new AccessDeniedException(attributeDenied.HandleUnauthorizedMessage()); + throw new AccessDeniedException(attributeDenied.HandleUnauthorizedMessage(), string.Format("[Plugin]::{0}::[Handler]", this.Id)); } var handler = (PluginWebHandler)Activator.CreateInstance(this.WebHandlerType); @@ -476,7 +476,7 @@ namespace Disco.Services.Plugins var fileDateCheck = System.IO.File.GetLastWriteTime(resourcePath); if (fileDateCheck == resourceHash.Item2) #endif - return new Tuple(resourcePath, resourceHash.Item1); + return new Tuple(resourcePath, resourceHash.Item1); } if (!File.Exists(resourcePath)) @@ -499,11 +499,11 @@ namespace Disco.Services.Plugins public string WebResourceUrl(string Resource) { var resourcePath = this.WebResourcePath(Resource); - + var url = UrlHelper.GenerateUrl("Plugin_Resources", null, null, new RouteValueDictionary(new Dictionary() { { "PluginId", this.Id }, { "res", Resource } }), RouteTable.Routes, HttpContext.Current.Request.RequestContext, false); - + url += string.Format("?v={0}", resourcePath.Item2); return url; diff --git a/Disco.Services/Plugins/PluginWebHandlerController.cs b/Disco.Services/Plugins/PluginWebHandlerController.cs index 15ba6695..8c0a5cc5 100644 --- a/Disco.Services/Plugins/PluginWebHandlerController.cs +++ b/Disco.Services/Plugins/PluginWebHandlerController.cs @@ -15,34 +15,42 @@ namespace Disco.Services.Plugins public override ActionResult ExecuteAction(string ActionName) { var handlerType = this.GetType(); - var methodDescriptor = FindControllerMethod(handlerType, ActionName); + var methodDescriptor = FindControllerMethod(Manifest, handlerType, ActionName); if (methodDescriptor == null) return this.HttpNotFound("Unknown Plugin Method"); - + // Authorize Method if (methodDescriptor.Authorizers.Length > 0) { var attributeDenied = methodDescriptor.Authorizers.FirstOrDefault(a => !a.IsAuthorized(HostController.HttpContext)); if (attributeDenied != null) - return new HttpUnauthorizedResult(attributeDenied.HandleUnauthorizedMessage()); + { + var message = attributeDenied.HandleUnauthorizedMessage(); + var resource = string.Format("{0} [{1}]", attributeDenied.AuthorizeResource, HostController.Request.RawUrl); + + if (CurrentUser != null) + AuthorizationLog.LogAccessDenied(CurrentUser.Id, resource, message); + + return new HttpUnauthorizedResult(message); + } } - var methodParams = BuildMethodParameters(handlerType, methodDescriptor.MethodInfo, ActionName, this.HostController); + var methodParams = BuildMethodParameters(Manifest, handlerType, methodDescriptor.MethodInfo, ActionName, this.HostController); return (ActionResult)methodDescriptor.MethodInfo.Invoke(this, methodParams); } - private static WebHandlerCachedItem FindControllerMethod(Type Handler, string ActionName) + private static WebHandlerCachedItem FindControllerMethod(PluginManifest Manifest, Type Handler, string ActionName) { - var descriptors = CacheWebHandler(Handler); + var descriptors = CacheWebHandler(Manifest, Handler); WebHandlerCachedItem method; if (descriptors.TryGetValue(ActionName.ToLower(), out method)) return method; // Not Found else return null; // Not Found } - private static object[] BuildMethodParameters(Type Handler, MethodInfo methodInfo, string ActionName, Controller HostController) + private static object[] BuildMethodParameters(PluginManifest Manifest, Type Handler, MethodInfo methodInfo, string ActionName, Controller HostController) { var methodParams = methodInfo.GetParameters(); var result = new object[methodParams.Length]; @@ -69,7 +77,7 @@ namespace Disco.Services.Plugins var parameterValue = modelBinder.BindModel(HostController.ControllerContext, bindingContext); if (parameterValue == null && methodParam.HasDefaultValue) - parameterValue = methodParam.DefaultValue; + parameterValue = methodParam.DefaultValue; result[i] = parameterValue; } @@ -79,7 +87,7 @@ namespace Disco.Services.Plugins #region Method Cache private static Dictionary> WebHandlerCachedItems = new Dictionary>(); - private static Dictionary CacheWebHandler(Type Handler) + private static Dictionary CacheWebHandler(PluginManifest Manifest, Type Handler) { Dictionary result; @@ -90,11 +98,15 @@ namespace Disco.Services.Plugins var methods = Array.FindAll(Handler.GetMethods(BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), mi => { return !mi.IsSpecialName && typeof(ActionResult).IsAssignableFrom(mi.ReturnType); }); foreach (var method in methods) { + var authorizers = method.GetCustomAttributes().ToArray(); + foreach (var authorizer in authorizers) + authorizer.AuthorizeResource = string.Format("[Plugin]::{0}::{1}", Manifest.Id, method.Name); + var item = new WebHandlerCachedItem() { Method = method.Name, MethodInfo = method, - Authorizers = method.GetCustomAttributes().ToArray() + Authorizers = authorizers }; result.Add(item.Method.ToLower(), item); } diff --git a/Disco.Services/Users/UserService.cs b/Disco.Services/Users/UserService.cs index 6d24a233..bb245188 100644 --- a/Disco.Services/Users/UserService.cs +++ b/Disco.Services/Users/UserService.cs @@ -159,6 +159,8 @@ namespace Disco.Services.Users Database.AuthorizationRoles.Add(Role); Database.SaveChanges(); + AuthorizationLog.LogRoleCreated(Role, CurrentUserId); + // Add to Cache RoleCache.AddRole(Role); @@ -175,6 +177,8 @@ namespace Disco.Services.Users Database.AuthorizationRoles.Remove(Role); Database.SaveChanges(); + AuthorizationLog.LogRoleDeleted(Role, CurrentUserId); + // Remove from Role Cache RoleCache.RemoveRole(Role); diff --git a/Disco.Web/Areas/API/Controllers/AuthorizationRoleController.cs b/Disco.Web/Areas/API/Controllers/AuthorizationRoleController.cs index d7079acd..d332ce6e 100644 --- a/Disco.Web/Areas/API/Controllers/AuthorizationRoleController.cs +++ b/Disco.Web/Areas/API/Controllers/AuthorizationRoleController.cs @@ -3,6 +3,7 @@ using Disco.BI.Interop.ActiveDirectory; using Disco.Models.Interop.ActiveDirectory; using Disco.Models.Repository; using Disco.Services.Authorization; +using Disco.Services.Authorization.Roles; using Disco.Services.Users; using Disco.Services.Web; using System; @@ -71,24 +72,37 @@ namespace Disco.Web.Areas.API.Controllers { throw new Exception("An Authorization Role with that name already exists"); } - + var oldRoleName = AuthorizationRole.Name; AuthorizationRole.Name = Name; UserService.UpdateAuthorizationRole(Database, AuthorizationRole); + AuthorizationLog.LogRoleConfiguredRenamed(AuthorizationRole, CurrentUser.Id, oldRoleName); } } } private void UpdateClaims(AuthorizationRole AuthorizationRole, string[] ClaimKeys) { - var claims = Claims.BuildClaims(ClaimKeys); - AuthorizationRole.SetClaims(claims); + var proposedClaims = Claims.BuildClaims(ClaimKeys); + + var currentToken = RoleToken.FromAuthorizationRole(AuthorizationRole); + var currentClaimKeys = Claims.GetClaimKeys(currentToken.Claims); + var removedClaims = currentClaimKeys.Except(ClaimKeys).ToArray(); + var addedClaims = ClaimKeys.Except(currentClaimKeys).ToArray(); + AuthorizationRole.SetClaims(proposedClaims); UserService.UpdateAuthorizationRole(Database, AuthorizationRole); + + if (removedClaims.Length > 0) + AuthorizationLog.LogRoleConfiguredClaimsRemoved(AuthorizationRole, CurrentUser.Id, removedClaims); + if (addedClaims.Length > 0) + AuthorizationLog.LogRoleConfiguredClaimsAdded(AuthorizationRole, CurrentUser.Id, addedClaims); } private void UpdateSubjects(AuthorizationRole AuthorizationRole, string[] Subjects) { string subjectIds = null; + string[] removedSubjects = null; + string[] addedSubjects = null; // Validate Subjects if (Subjects != null && Subjects.Length > 0) @@ -99,7 +113,12 @@ namespace Disco.Web.Areas.API.Controllers if (invalidSubjects.Count > 0) throw new ArgumentException(string.Format("Subjects not found: {0}", string.Join(", ", invalidSubjects)), "Subjects"); - subjectIds = string.Join(",", subjects.Select(s => s.Item2.SamAccountName).OrderBy(s => s)); + var proposedSubjects = subjects.Select(s => s.Item2.SamAccountName).OrderBy(s => s).ToArray(); + var currentSubjects = AuthorizationRole.SubjectIds.Split(','); + removedSubjects = currentSubjects.Except(proposedSubjects).ToArray(); + addedSubjects = proposedSubjects.Except(currentSubjects).ToArray(); + + subjectIds = string.Join(",", proposedSubjects); if (string.IsNullOrEmpty(subjectIds)) subjectIds = null; @@ -109,6 +128,11 @@ namespace Disco.Web.Areas.API.Controllers { AuthorizationRole.SubjectIds = subjectIds; UserService.UpdateAuthorizationRole(Database, AuthorizationRole); + + if (removedSubjects != null && removedSubjects.Length > 0) + AuthorizationLog.LogRoleConfiguredSubjectsRemoved(AuthorizationRole, CurrentUser.Id, removedSubjects); + if (addedSubjects != null && addedSubjects.Length > 0) + AuthorizationLog.LogRoleConfiguredSubjectsAdded(AuthorizationRole, CurrentUser.Id, addedSubjects); } } @@ -189,7 +213,6 @@ namespace Disco.Web.Areas.API.Controllers if (ar != null) { ar.Delete(Database); - Database.SaveChanges(); if (redirect.HasValue && redirect.Value) return RedirectToAction(MVC.Config.AuthorizationRole.Index(null)); diff --git a/Disco.Web/Controllers/PluginWebHandlerController.cs b/Disco.Web/Controllers/PluginWebHandlerController.cs index df1d98ef..5ed31489 100644 --- a/Disco.Web/Controllers/PluginWebHandlerController.cs +++ b/Disco.Web/Controllers/PluginWebHandlerController.cs @@ -6,6 +6,7 @@ using System.Web; using System.Web.Mvc; using Disco.Services.Plugins; using Disco.Services.Authorization; +using Disco.Services.Users; namespace Disco.Web.Controllers { @@ -28,6 +29,9 @@ namespace Disco.Web.Controllers } catch (AccessDeniedException accessDeniedException) { + if (UserService.CurrentUserId != null) + AuthorizationLog.LogAccessDenied(UserService.CurrentUserId, string.Format("{0} [{1}]", accessDeniedException.Resource, Request.RawUrl), accessDeniedException.Message); + return new HttpUnauthorizedResult(accessDeniedException.Message); } } diff --git a/Disco.Web/Global.asax.cs b/Disco.Web/Global.asax.cs index 13e7ff8c..69e787f7 100644 --- a/Disco.Web/Global.asax.cs +++ b/Disco.Web/Global.asax.cs @@ -1,17 +1,14 @@ -using System; -using System.Collections.Generic; +using Disco.Data.Repository; +using Disco.Services.Authorization; +using Disco.Services.Users; +using System; using System.Configuration; using System.Diagnostics; -using System.Linq; using System.Net; -using System.Security.Principal; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Routing; -using Disco.Data.Repository; -using Disco.Models.Repository; -using Disco.Services.Users; namespace Disco.Web { @@ -219,7 +216,22 @@ namespace Disco.Web { try { - Disco.Services.Logging.SystemLog.LogException("Global Application Exception Caught", Server.GetLastError()); + var ex = Server.GetLastError(); + + if (ex is AccessDeniedException) + { + var accessDeniedException = (AccessDeniedException)ex; + var resource = accessDeniedException.Resource; + var httpContext = HttpContext.Current; + if (httpContext != null && httpContext.Request != null) + resource = string.Format("{0} [{1}]", resource, httpContext.Request.RawUrl); + + AuthorizationLog.LogAccessDenied(UserService.CurrentUserId ?? "[Anonymous]", resource, accessDeniedException.Message); + } + else + { + Disco.Services.Logging.SystemLog.LogException("Global Application Exception Caught", ex); + } } catch (Exception) {