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