/// <summary>
 /// Remove the specified object
 /// </summary>
 public object Remove(Type scopingType, object scopingKey, object key)
 {
     if (key == null || key.Equals("*"))
     {
         this.m_queueService.Purge((String)scopingKey);
         AuditUtil.SendAudit(new Core.Auditing.AuditData()
                             .WithLocalDevice()
                             .WithUser()
                             .WithAction(Core.Auditing.ActionType.Delete)
                             .WithEventIdentifier(Core.Auditing.EventIdentifierType.ApplicationActivity)
                             .WithOutcome(Core.Auditing.OutcomeIndicator.Success)
                             .WithTimestamp(DateTimeOffset.Now)
                             .WithEventType("PurgeQueue")
                             .WithHttpInformation(RestOperationContext.Current.IncomingRequest)
                             .WithSystemObjects(Core.Auditing.AuditableObjectRole.Resource, Core.Auditing.AuditableObjectLifecycle.PermanentErasure, new Uri($"urn:santedb:org:DispatcherQueueInfo/{scopingKey}/event/*")));
     }
     else
     {
         var data = this.m_queueService.DequeueById((String)scopingKey, (string)key);
         AuditUtil.SendAudit(new Core.Auditing.AuditData()
                             .WithLocalDevice()
                             .WithUser()
                             .WithAction(Core.Auditing.ActionType.Delete)
                             .WithEventIdentifier(Core.Auditing.EventIdentifierType.ApplicationActivity)
                             .WithOutcome(Core.Auditing.OutcomeIndicator.Success)
                             .WithTimestamp(DateTimeOffset.Now)
                             .WithEventType("PurgeQueueObject")
                             .WithHttpInformation(RestOperationContext.Current.IncomingRequest)
                             .WithSystemObjects(Core.Auditing.AuditableObjectRole.Resource, Core.Auditing.AuditableObjectLifecycle.PermanentErasure, new Uri($"urn:santedb:org:DispatcherQueueInfo/{scopingKey}/event/{key}")));
     }
     return(null);
 }
        /// <summary>
        /// Synchronized dispatch service
        /// </summary>
        public SynchronizedAuditDispatchService(IConfigurationManager configurationManager, IJobStateManagerService jobStateManager, IJobManagerService scheduleManager, IThreadPoolService threadPool, IQueueManagerService queueManagerService)
        {
            this.m_securityConfiguration = configurationManager.GetSection <SecurityConfigurationSection>();
            this.m_jobStateManager       = jobStateManager;
            this.m_queueManagerService   = queueManagerService;

            if (!scheduleManager.GetJobSchedules(this).Any())
            {
                scheduleManager.SetJobSchedule(this, new TimeSpan(0, 5, 0));
            }

            threadPool.QueueUserWorkItem(_ =>
            {
                try
                {
                    AuditData securityAlertData = new AuditData(DateTime.Now, ActionType.Execute, OutcomeIndicator.Success, EventIdentifierType.SecurityAlert, AuditUtil.CreateAuditActionCode(EventTypeCodes.AuditLoggingStarted));
                    AuditUtil.AddLocalDeviceActor(securityAlertData);
                    AuditUtil.SendAudit(securityAlertData);
                }
                catch (Exception ex)
                {
                    this.m_tracer.TraceError("Error starting up audit repository service: {0}", ex);
                }
            });
        }
        /// <summary>
        /// Dispose of the job
        /// </summary>
        public void Dispose()
        {
            AuditData securityAlertData = new AuditData(DateTime.Now, ActionType.Execute, OutcomeIndicator.Success, EventIdentifierType.SecurityAlert, AuditUtil.CreateAuditActionCode(EventTypeCodes.AuditLoggingStopped));

            AuditUtil.AddLocalDeviceActor(securityAlertData);
            AuditUtil.SendAudit(securityAlertData);
        }
Beispiel #4
0
        /// <summary>
        /// Start auditor service
        /// </summary>
        public bool Start()
        {
            this.Starting?.Invoke(this, EventArgs.Empty);

            this.m_safeToStop = false;
            ApplicationServiceContext.Current.Stopping += (o, e) =>
            {
                this.m_safeToStop = true;
                this.Stop();
            };
            ApplicationServiceContext.Current.Started += (o, e) =>
            {
                try
                {
                    this.m_tracer.TraceInfo("Binding to service events...");

                    if (ApplicationServiceContext.Current.GetService <IIdentityProviderService>() != null)
                    {
                        ApplicationServiceContext.Current.GetService <IIdentityProviderService>().Authenticated += (so, se) =>
                        {
                            AuditUtil.AuditLogin(se.Principal, se.UserName, so as IIdentityProviderService, se.Success);
                        }
                    }
                    ;
                    if (ApplicationServiceContext.Current.GetService <ISessionProviderService>() != null)
                    {
                        ApplicationServiceContext.Current.GetService <ISessionProviderService>().Established += (so, se) =>
                        {
                            if (se.Elevated)
                            {
                                AuditUtil.AuditOverride(se.Session, se.Principal, se.Purpose, se.Policies, se.Success);
                            }
                            AuditUtil.AuditSessionStart(se.Session, se.Principal, se.Success);
                        };
                        ApplicationServiceContext.Current.GetService <ISessionProviderService>().Abandoned += (so, se) => AuditUtil.AuditSessionStop(se.Session, se.Principal, se.Success);
                    }
                    // Audit that Audits are now being recorded
                    var audit = new AuditData(DateTimeOffset.Now, ActionType.Execute, OutcomeIndicator.Success, EventIdentifierType.ApplicationActivity, AuditUtil.CreateAuditActionCode(EventTypeCodes.AuditLoggingStarted));
                    AuditUtil.AddLocalDeviceActor(audit);
                    AuditUtil.SendAudit(audit);
                }
                catch (Exception ex)
                {
                    this.m_tracer.TraceError("Error starting up audit repository service: {0}", ex);
                }
            };

            this.Started?.Invoke(this, EventArgs.Empty);
            return(true);
        }
Beispiel #5
0
 public object Obsolete(object key)
 {
     this.m_queueService.Purge((String)key);
     AuditUtil.SendAudit(new Core.Auditing.AuditData()
                         .WithLocalDevice()
                         .WithUser()
                         .WithAction(Core.Auditing.ActionType.Delete)
                         .WithEventIdentifier(Core.Auditing.EventIdentifierType.ApplicationActivity)
                         .WithOutcome(Core.Auditing.OutcomeIndicator.Success)
                         .WithTimestamp(DateTimeOffset.Now)
                         .WithEventType("PurgeQueue")
                         .WithHttpInformation(RestOperationContext.Current.IncomingRequest)
                         .WithSystemObjects(Core.Auditing.AuditableObjectRole.Resource, Core.Auditing.AuditableObjectLifecycle.PermanentErasure, new Uri($"urn:santedb:org:DispatcherQueueInfo/{key}/event")));
     return(null);
 }
Beispiel #6
0
        /// <summary>
        /// Stopped
        /// </summary>
        public bool Stop()
        {
            this.Stopping?.Invoke(this, EventArgs.Empty);

            // Audit tool should never stop!!!!!
            if (!this.m_safeToStop)
            {
                AuditData securityAlertData = new AuditData(DateTimeOffset.Now, ActionType.Execute, OutcomeIndicator.EpicFail, EventIdentifierType.SecurityAlert, AuditUtil.CreateAuditActionCode(EventTypeCodes.AuditLoggingStopped));
                AuditUtil.AddLocalDeviceActor(securityAlertData);
                AuditUtil.SendAudit(securityAlertData);
            }
            else
            {
                // Audit that audits are no longer being recorded
                var audit = new AuditData(DateTimeOffset.Now, ActionType.Execute, OutcomeIndicator.Success, EventIdentifierType.ApplicationActivity, AuditUtil.CreateAuditActionCode(EventTypeCodes.AuditLoggingStopped));
                AuditUtil.AddLocalDeviceActor(audit);
                AuditUtil.SendAudit(audit);
            };

            return(true);
        }
Beispiel #7
0
        /// <summary>
        /// Audit an operation that was exceuted
        /// </summary>
        private void AuditOperationAction(string resourceType, string operationName, OutcomeIndicator outcome, params Resource[] objects)
        {
            var audit = new AuditData(DateTime.Now, ActionType.Execute, outcome, EventIdentifierType.ApplicationActivity, new AuditCode(Hl7.Fhir.Utility.EnumUtility.GetLiteral(SystemRestfulInteraction.Batch), "http://hl7.org/fhir/ValueSet/system-restful-interaction"));

            AuditUtil.AddLocalDeviceActor(audit);
            AuditUtil.AddUserActor(audit);

            var handler = ExtensionUtil.GetOperation(resourceType, operationName);

            audit.AuditableObjects.Add(new AuditableObject()
            {
                IDTypeCode = AuditableObjectIdType.Uri,
                ObjectId   = handler?.Uri.ToString() ?? $"urn:uuid:{Guid.Empty}",
                QueryData  = RestOperationContext.Current?.IncomingRequest.Url.ToString(),
                ObjectData = RestOperationContext.Current?.IncomingRequest.Headers.AllKeys.Select(o => new ObjectDataExtension(o, RestOperationContext.Current.IncomingRequest.Headers.Get(o))).ToList(),
                Role       = AuditableObjectRole.Job,
                Type       = AuditableObjectType.SystemObject
            });

            audit.AuditableObjects.AddRange(objects.SelectMany(o => this.CreateAuditObjects(o, AuditableObjectLifecycle.NotSet)));
            AuditUtil.SendAudit(audit);
        }
        /// <summary>
        /// Query for all entries on the specified queue
        /// </summary>
        public IEnumerable <object> Query(Type scopingType, object scopingKey, NameValueCollection filter, int offset, int count, out int totalCount)
        {
            var entries = this.m_queueService.GetQueueEntries((string)scopingKey);

            if (filter.TryGetValue("name", out var values))
            {
                entries = entries.Where(o => o.CorrelationId.Contains(values.First().Replace("*", "")));
            }
            totalCount = entries.Count();

            AuditUtil.SendAudit(new Core.Auditing.AuditData()
                                .WithLocalDevice()
                                .WithUser()
                                .WithAction(Core.Auditing.ActionType.Execute)
                                .WithEventIdentifier(Core.Auditing.EventIdentifierType.Query)
                                .WithOutcome(Core.Auditing.OutcomeIndicator.Success)
                                .WithTimestamp(DateTimeOffset.Now)
                                .WithEventType("QueryQueueObject")
                                .WithHttpInformation(RestOperationContext.Current.IncomingRequest)
                                .WithSystemObjects(Core.Auditing.AuditableObjectRole.Resource, Core.Auditing.AuditableObjectLifecycle.PermanentErasure, entries.Select(o => new Uri($"urn:santedb:org:DispatcherQueueInfo/{scopingKey}/entry/{o.CorrelationId}")).ToArray()));

            return(entries.Skip(offset).Take(count));
        }
 /// <summary>
 /// Adds an entry - or rather - moves it
 /// </summary>
 public object Add(Type scopingType, object scopingKey, object item)
 {
     if (item is DispatcherQueueEntry dqe)
     {
         var retVal = this.m_queueService.Move(dqe, (string)scopingKey);
         AuditUtil.SendAudit(new Core.Auditing.AuditData()
                             .WithLocalDevice()
                             .WithUser()
                             .WithAction(Core.Auditing.ActionType.Update)
                             .WithEventIdentifier(Core.Auditing.EventIdentifierType.Import)
                             .WithOutcome(Core.Auditing.OutcomeIndicator.Success)
                             .WithTimestamp(DateTimeOffset.Now)
                             .WithEventType("MoveQueueObject")
                             .WithHttpInformation(RestOperationContext.Current.IncomingRequest)
                             .WithSystemObjects(Core.Auditing.AuditableObjectRole.Resource, Core.Auditing.AuditableObjectLifecycle.Archiving, new Uri($"urn:santedb:org:DispatcherQueueInfo/{dqe.SourceQueue}/entry/{dqe.CorrelationId}"))
                             .WithSystemObjects(Core.Auditing.AuditableObjectRole.Resource, Core.Auditing.AuditableObjectLifecycle.Creation, new Uri($"urn:santedb:org:DispatcherQueueInfo/{scopingKey}/entry/{retVal.CorrelationId}"))
                             );
         return(retVal);
     }
     else
     {
         throw new ArgumentException(nameof(item), "Expected DispatcherQueueEntry body");
     }
 }
Beispiel #10
0
        /// <summary>
        /// Audit data action on FHIR interface
        /// </summary>
        private void AuditDataAction(TypeRestfulInteraction type, OutcomeIndicator outcome, params Resource[] objects)
        {
            AuditData audit = new AuditData(DateTime.Now, ActionType.Execute, outcome, EventIdentifierType.ApplicationActivity, new AuditCode(Hl7.Fhir.Utility.EnumUtility.GetLiteral(type), "http://hl7.org/fhir/ValueSet/type-restful-interaction"));
            AuditableObjectLifecycle lifecycle = AuditableObjectLifecycle.NotSet;

            switch (type)
            {
            case TypeRestfulInteraction.Create:
                audit.ActionCode      = ActionType.Create;
                audit.EventIdentifier = EventIdentifierType.Import;
                lifecycle             = AuditableObjectLifecycle.Creation;
                break;

            case TypeRestfulInteraction.Delete:
                audit.ActionCode      = ActionType.Delete;
                audit.EventIdentifier = EventIdentifierType.Import;
                lifecycle             = AuditableObjectLifecycle.LogicalDeletion;
                break;

            case TypeRestfulInteraction.HistoryInstance:
            case TypeRestfulInteraction.HistoryType:
            case TypeRestfulInteraction.SearchType:
                audit.ActionCode      = ActionType.Execute;
                audit.EventIdentifier = EventIdentifierType.Query;
                lifecycle             = AuditableObjectLifecycle.Disclosure;
                audit.AuditableObjects.Add(new AuditableObject()
                {
                    QueryData  = RestOperationContext.Current?.IncomingRequest.Url.ToString(),
                    Role       = AuditableObjectRole.Query,
                    Type       = AuditableObjectType.SystemObject,
                    ObjectData = RestOperationContext.Current?.IncomingRequest.Headers.AllKeys.Where(o => o.Equals("accept", StringComparison.OrdinalIgnoreCase)).Select(o => new ObjectDataExtension(o, RestOperationContext.Current.IncomingRequest.Headers.Get(o))).ToList()
                });
                break;

            case TypeRestfulInteraction.Update:
            case TypeRestfulInteraction.Patch:
                audit.ActionCode      = ActionType.Update;
                audit.EventIdentifier = EventIdentifierType.Import;
                lifecycle             = AuditableObjectLifecycle.Amendment;
                break;

            case TypeRestfulInteraction.Vread:
            case TypeRestfulInteraction.Read:
                audit.ActionCode      = ActionType.Read;
                audit.EventIdentifier = EventIdentifierType.Query;
                lifecycle             = AuditableObjectLifecycle.Disclosure;
                audit.AuditableObjects.Add(new AuditableObject()
                {
                    QueryData  = RestOperationContext.Current?.IncomingRequest.Url.ToString(),
                    Role       = AuditableObjectRole.Query,
                    Type       = AuditableObjectType.SystemObject,
                    ObjectData = RestOperationContext.Current?.IncomingRequest.Headers.AllKeys.Where(o => o.Equals("accept", StringComparison.OrdinalIgnoreCase)).Select(o => new ObjectDataExtension(o, RestOperationContext.Current.IncomingRequest.Headers.Get(o))).ToList()
                });
                break;
            }

            AuditUtil.AddLocalDeviceActor(audit);
            AuditUtil.AddUserActor(audit);

            audit.AuditableObjects.AddRange(objects.SelectMany(o => this.CreateAuditObjects(o, lifecycle)));

            AuditUtil.SendAudit(audit);
        }
        /// <summary>
        /// Hydrate the query
        /// </summary>
        /// <param name="queryId"></param>
        /// <returns></returns>
        private BisResultContext HydrateQuery(String queryId)
        {
            AuditData audit = new AuditData(DateTimeOffset.Now, ActionType.Execute, OutcomeIndicator.Success, EventIdentifierType.Query, AuditUtil.CreateAuditActionCode(EventTypeCodes.SecondaryUseQuery));

            try
            {
                // First we want to grab the appropriate source for this ID
                var viewDef = this.m_metadataRepository.Get <BiViewDefinition>(queryId);
                if (viewDef == null)
                {
                    var queryDef = this.m_metadataRepository.Get <BiQueryDefinition>(queryId);
                    if (queryDef == null) // Parameter value
                    {
                        var parmDef = this.m_metadataRepository.Get <BiParameterDefinition>(queryId);
                        if (parmDef == null)
                        {
                            throw new KeyNotFoundException($"Could not find a Parameter, Query or View to hydrate named {queryId}");
                        }
                        queryDef    = parmDef?.Values as BiQueryDefinition;
                        queryDef.Id = queryDef.Id ?? queryId;
                    }

                    viewDef = new BiViewDefinition()
                    {
                        Id    = queryDef.Id,
                        Query = queryDef
                    };
                }

                viewDef = SanteDB.BI.Util.BiUtils.ResolveRefs(viewDef) as BiViewDefinition;
                var dsource = viewDef.Query?.DataSources.FirstOrDefault(o => o.Name == "main") ?? viewDef.Query?.DataSources.FirstOrDefault();
                if (dsource == null)
                {
                    throw new KeyNotFoundException("Query does not contain a data source");
                }

                IBiDataSource providerImplementation = null;
                if (dsource.ProviderType != null && this.m_metadataRepository.IsLocal)
                {
                    providerImplementation = this.m_serviceManager.CreateInjected(dsource.ProviderType) as IBiDataSource;
                }
                else
                {
                    providerImplementation = ApplicationServiceContext.Current.GetService <IBiDataSource>(); // Global default
                }
                // Populate data about the query
                audit.AuditableObjects.Add(new AuditableObject()
                {
                    IDTypeCode    = AuditableObjectIdType.ReportNumber,
                    LifecycleType = AuditableObjectLifecycle.Report,
                    ObjectId      = queryId,
                    QueryData     = RestOperationContext.Current.IncomingRequest.Url.Query,
                    Role          = AuditableObjectRole.Query,
                    Type          = AuditableObjectType.SystemObject
                });

                var parameters = this.CreateParameterDictionary();

                // Aggregations and groups?
                if (RestOperationContext.Current.IncomingRequest.QueryString["_groupBy"] != null)
                {
                    var aggRx = new Regex(@"(\w*)\((.*?)\)");
                    viewDef.AggregationDefinitions = new List <BiAggregationDefinition>()
                    {
                        new BiAggregationDefinition()
                        {
                            Groupings = RestOperationContext.Current.IncomingRequest.QueryString.GetValues("_groupBy").Select(o => new BiSqlColumnReference()
                            {
                                ColumnSelector = o.Contains("::")  ? $"CAST({o.Substring(0, o.IndexOf(":"))} AS {o.Substring(o.IndexOf(":") + 2)})" : o,
                                Name           = o.Contains("::") ? o.Substring(0, o.IndexOf(":")) : o
                            }).ToList(),
                            Columns = RestOperationContext.Current.IncomingRequest.QueryString.GetValues("_select").Select(o => {
                                var match = aggRx.Match(o);
                                if (!match.Success)
                                {
                                    throw new InvalidOperationException("Aggregation function must be in format AGGREGATOR(COLUMN)");
                                }
                                return(new BiAggregateSqlColumnReference()
                                {
                                    Aggregation = (BiAggregateFunction)Enum.Parse(typeof(BiAggregateFunction), match.Groups[1].Value, true),
                                    ColumnSelector = match.Groups[2].Value,
                                    Name = match.Groups[2].Value
                                });
                            }).ToList()
                        }
                    };
                }

                int offset = 0, count = 100;
                if (!Int32.TryParse(RestOperationContext.Current.IncomingRequest.QueryString["_offset"] ?? "0", out offset))
                {
                    throw new FormatException("_offset is not in the correct format");
                }
                if (!Int32.TryParse(RestOperationContext.Current.IncomingRequest.QueryString["_count"] ?? "100", out count))
                {
                    throw new FormatException("_count is not in the correct format");
                }


                var queryData = providerImplementation.ExecuteView(viewDef, parameters, offset, count);
                return(queryData);
            }
            catch (KeyNotFoundException)
            {
                audit.Outcome = OutcomeIndicator.MinorFail;
                throw;
            }
            catch (Exception e)
            {
                audit.Outcome = OutcomeIndicator.MinorFail;
                this.m_tracer.TraceError("Error rendering query: {0}", e);
                throw new FaultException(500, $"Error rendering query {queryId}", e);
            }
            finally
            {
                AuditUtil.AddLocalDeviceActor(audit);
                AuditUtil.AddUserActor(audit);
                AuditUtil.SendAudit(audit);
            }
        }
Beispiel #12
0
        /// <summary>
        /// Process a message received by the syslog message handler
        /// </summary>
        private void ProcessMessage(Syslog.TransportProtocol.SyslogMessageReceivedEventArgs e)
        {
            try
            {
                if (e == null || e.Message == null)
                {
                    this.m_traceSource.TraceWarning("Received null SyslogEvent from transport");
                    return;
                }


                // Secured copy
                AuthenticatedSyslogMessageReceivedEventArgs securedEvent = e as AuthenticatedSyslogMessageReceivedEventArgs;

                // Process a result
                ApplicationServiceContext.Current.GetService <IThreadPoolService>().QueueUserWorkItem((p) =>
                {
                    using (AuthenticationContext.EnterSystemContext())
                    {
                        try
                        {
                            var processResult = (ParseAuditResult)p;

                            // Now does the audit persistence service exist?
                            if (ApplicationServiceContext.Current.GetService <IRepositoryService <AuditBundle> >() != null)
                            {
                                AuditBundle insertBundle = new AuditBundle();
                                Audit audit = processResult.Message.ToAudit();

                                // Is this an error?
                                if (audit != null)
                                {
                                    bool alertStatus = false;

                                    // Set core properties
                                    audit.CorrelationToken = processResult.SourceMessage.CorrelationId;

                                    Uri solicitorEp = new Uri(String.Format("atna://{0}", e.SolicitorEndpoint.Host)),
                                    receiveEp       = new Uri(String.Format("atna://{0}", e.ReceiveEndpoint.Host));

                                    // Create or get node
                                    int tr         = 0;
                                    var senderNode = ApplicationServiceContext.Current.GetService <IRepositoryService <AuditNode> >().Find(o => o.HostName == e.Message.HostName.ToLower(), 0, 1, out tr).FirstOrDefault();
                                    if (senderNode == null) // Flag alert
                                    {
                                        alertStatus = true;
                                        processResult.Details.Add(new DetectedIssue(DetectedIssuePriorityType.Warning, "sender.unknown", $"The sender {e.Message.HostName} is unknown", DetectedIssueKeys.SecurityIssue));
                                        senderNode = new AuditNode()
                                        {
                                            Key               = Guid.NewGuid(),
                                            HostName          = e.Message.HostName.ToLower(),
                                            Name              = e.Message.HostName,
                                            Status            = AuditStatusType.New,
                                            SecurityDeviceKey = ApplicationServiceContext.Current.GetService <IRepositoryService <SecurityDevice> >().Find(o => o.Name == e.Message.HostName, 0, 1, out tr).FirstOrDefault()?.Key.Value
                                        };
                                        insertBundle.Add(senderNode);
                                    }

                                    var receiverNode = insertBundle.Item.OfType <AuditNode>().FirstOrDefault(o => o.HostName == Environment.MachineName.ToLower()) ??
                                                       ApplicationServiceContext.Current.GetService <IRepositoryService <AuditNode> >().Find(o => o.HostName == Environment.MachineName.ToLower(), 0, 1, out tr).FirstOrDefault();

                                    if (receiverNode == null) // Flag alert
                                    {
                                        alertStatus = true;
                                        processResult.Details.Add(new DetectedIssue(DetectedIssuePriorityType.Warning, "receiver.unknown", $"The receiver {Environment.MachineName} is not registered to receive messages", DetectedIssueKeys.SecurityIssue));
                                        receiverNode = new AuditNode()
                                        {
                                            Key               = Guid.NewGuid(),
                                            HostName          = Environment.MachineName.ToLower(),
                                            Name              = Environment.MachineName,
                                            Status            = AuditStatusType.New,
                                            SecurityDeviceKey = ApplicationServiceContext.Current.GetService <IRepositoryService <SecurityDevice> >().Find(o => o.Name == Environment.MachineName, 0, 1, out tr).FirstOrDefault()?.Key.Value
                                        };
                                        insertBundle.Add(receiverNode);
                                    }

                                    // Create or get session
                                    var session = ApplicationServiceContext.Current.GetService <IRepositoryService <AuditSession> >().Get(processResult.SourceMessage.SessionId);
                                    if (session == null)
                                    {
                                        insertBundle.Add(new AuditSession()
                                        {
                                            Key               = processResult.SourceMessage.SessionId,
                                            Receiver          = receiverNode,
                                            Sender            = senderNode,
                                            ReceivingEndpoint = receiveEp.ToString(),
                                            SenderEndpoint    = solicitorEp.ToString()
                                        });
                                    }

                                    // Get the bundle ready ...
                                    audit.CorrelationToken = processResult.SourceMessage.CorrelationId;
                                    audit.IsAlert          = alertStatus;
                                    audit.ProcessId        = e.Message.ProcessId;
                                    audit.ProcessName      = e.Message.ProcessName;
                                    audit.CreationTime     = e.Timestamp;
                                    audit.SessionKey       = processResult.SourceMessage.SessionId;
                                    audit.Status           = AuditStatusType.New;
                                    audit.Details          = processResult.Details?.Select(i => new AuditDetailData()
                                    {
                                        Key       = Guid.NewGuid(),
                                        Message   = i.Text,
                                        IssueType = (DetectedIssuePriorityType)Enum.Parse(typeof(DetectedIssuePriorityType), i.Priority.ToString())
                                    }).ToList();
                                    insertBundle.Add(audit);
                                }
                                else if (processResult.Details.Count() > 0)
                                {
                                    foreach (var i in processResult.Details.Where(o => o.Priority != DetectedIssuePriorityType.Information))
                                    {
                                        insertBundle.Add(new AuditDetailData()
                                        {
                                            Key             = Guid.NewGuid(),
                                            SourceEntityKey = audit.CorrelationToken,
                                            Message         = i.Text,
                                            IssueType       = i.Priority == DetectedIssuePriorityType.Error ? DetectedIssuePriorityType.Error : DetectedIssuePriorityType.Warning
                                        });
                                    }
                                }

                                // Batch persistence service
                                ApplicationServiceContext.Current.GetService <IRepositoryService <AuditBundle> >().Insert(insertBundle);
                            }
                            else
                            {
                                // Use "classic" mode
                                AuditData audit = processResult.Message.ToAuditData();

                                audit.AddMetadata(AuditMetadataKey.LocalEndpoint, e.ReceiveEndpoint.ToString());
                                audit.AddMetadata(AuditMetadataKey.ProcessName, e.Message.ProcessName);
                                audit.AddMetadata(AuditMetadataKey.RemoteHost, e.SolicitorEndpoint.ToString());
                                audit.AddMetadata(AuditMetadataKey.SessionId, e.Message.SessionId.ToString());
                                audit.AddMetadata(AuditMetadataKey.SubmissionTime, e.Message.Timestamp.ToString("o"));

                                AuditUtil.SendAudit(audit);
                            }
                        }
                        catch (Exception ex)
                        {
                            this.m_traceSource.TraceError("Error persisting audit: {0}", ex);
                        }
                    }
                }, MessageUtil.ParseAudit(e.Message));
            }
            catch (Exception ex)
            {
                this.m_traceSource.TraceError(ex.ToString());
                throw;
            }
        }