/// <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); }
/// <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); }
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); }
/// <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); }
/// <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"); } }
/// <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); } }
/// <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; } }