private void Log(AuditAction auditAction, HttpStatusCode?statusCode, HttpContext httpContext, IClaimsExtractor claimsExtractor)
        {
            IFhirRequestContext fhirRequestContext = _fhirRequestContextAccessor.RequestContext;

            string auditEventType = fhirRequestContext.AuditEventType;

            // We are retaining AuditEventType when CustomError occurs. Below check ensures that the audit log is not entered for the custom error request
            httpContext.Request.RouteValues.TryGetValue("action", out object actionName);
            if (!string.IsNullOrEmpty(actionName?.ToString()) && KnownRoutes.CustomError.Contains(actionName?.ToString(), StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            // Audit the call if an audit event type is associated with the action.
            // Since AuditEventType holds value for both AuditEventType and FhirAnonymousOperationType ensure that we only log the AuditEventType
            if (!string.IsNullOrEmpty(auditEventType) && !FhirAnonymousOperationTypeList.Contains(auditEventType, StringComparer.OrdinalIgnoreCase))
            {
                _auditLogger.LogAudit(
                    auditAction,
                    operation: auditEventType,
                    resourceType: fhirRequestContext.ResourceType,
                    requestUri: fhirRequestContext.Uri,
                    statusCode: statusCode,
                    correlationId: fhirRequestContext.CorrelationId,
                    callerIpAddress: httpContext.Connection?.RemoteIpAddress?.ToString(),
                    callerClaims: claimsExtractor.Extract(),
                    customHeaders: _auditHeaderReader.Read(httpContext));
            }
        }
Exemple #2
0
        public async Task <CreateExportResponse> Handle(CreateExportRequest request, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(request, nameof(request));

            if (await _authorizationService.CheckAccess(DataActions.Export) != DataActions.Export)
            {
                throw new UnauthorizedFhirActionException();
            }

            IReadOnlyCollection <KeyValuePair <string, string> > requestorClaims = _claimsExtractor.Extract()?
                                                                                   .OrderBy(claim => claim.Key, StringComparer.Ordinal).ToList();

            // Compute the hash of the job.
            var hashObject = new
            {
                request.RequestUri,
                RequestorClaims = requestorClaims,
            };

            string hash = JsonConvert.SerializeObject(hashObject).ComputeHash();

            // Check to see if a matching job exists or not. If a matching job exists, we will return that instead.
            // Otherwise, we will create a new export job. This will be a best effort since the likelihood of this happen should be small.
            ExportJobOutcome outcome = await _fhirOperationDataStore.GetExportJobByHashAsync(hash, cancellationToken);

            if (outcome == null)
            {
                var jobRecord = new ExportJobRecord(request.RequestUri, request.ResourceType, hash, requestorClaims, request.Since);

                outcome = await _fhirOperationDataStore.CreateExportJobAsync(jobRecord, cancellationToken);
            }

            return(new CreateExportResponse(outcome.JobRecord.Id));
        }
Exemple #3
0
        public async Task <CreateExportResponse> Handle(CreateExportRequest request, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(request, nameof(request));

            if (await _authorizationService.CheckAccess(DataActions.Export, cancellationToken) != DataActions.Export)
            {
                throw new UnauthorizedFhirActionException();
            }

            IReadOnlyCollection <KeyValuePair <string, string> > requestorClaims = _claimsExtractor.Extract()?
                                                                                   .OrderBy(claim => claim.Key, StringComparer.Ordinal).ToList();

            // Compute the hash of the job.
            var hashObject = new
            {
                request.RequestUri,
                RequestorClaims = requestorClaims,
            };

            string hash = JsonConvert.SerializeObject(hashObject).ComputeHash();

            string storageAccountConnectionHash = string.IsNullOrEmpty(_exportJobConfiguration.StorageAccountConnection) ?
                                                  string.Empty :
                                                  StringExtensions.ComputeHash(_exportJobConfiguration.StorageAccountConnection);

            // Check to see if a matching job exists or not. If a matching job exists, we will return that instead.
            // Otherwise, we will create a new export job. This will be a best effort since the likelihood of this happen should be small.
            ExportJobOutcome outcome = await _fhirOperationDataStore.GetExportJobByHashAsync(hash, cancellationToken);

            var filters = ParseFilter(request.Filters);

            ExportJobFormatConfiguration formatConfiguration = ParseFormat(request.FormatName, request.ContainerName != null);

            if (outcome == null)
            {
                var jobRecord = new ExportJobRecord(
                    request.RequestUri,
                    request.RequestType,
                    formatConfiguration.Format,
                    request.ResourceType,
                    filters,
                    hash,
                    _exportJobConfiguration.RollingFileSizeInMB,
                    requestorClaims,
                    request.Since,
                    request.GroupId,
                    storageAccountConnectionHash,
                    _exportJobConfiguration.StorageAccountUri,
                    request.AnonymizationConfigurationLocation,
                    request.AnonymizationConfigurationFileETag,
                    _exportJobConfiguration.MaximumNumberOfResourcesPerQuery,
                    _exportJobConfiguration.NumberOfPagesPerCommit,
                    request.ContainerName);

                outcome = await _fhirOperationDataStore.CreateExportJobAsync(jobRecord, cancellationToken);
            }

            return(new CreateExportResponse(outcome.JobRecord.Id));
        }
        public AuditHelperTests()
        {
            _dicomRequestContext.Uri.Returns(Uri);
            _dicomRequestContext.CorrelationId.Returns(CorrelationId);

            _dicomRequestContextAccessor.RequestContext = _dicomRequestContext;

            _httpContext.Connection.RemoteIpAddress = CallerIpAddress;

            _claimsExtractor.Extract().Returns(Claims);

            _auditHelper = new AuditHelper(_dicomRequestContextAccessor, _auditLogger, _auditHeaderReader);
        }
        public AuditHelperTests()
        {
            _fhirRequestContext.Uri.Returns(Uri);
            _fhirRequestContext.CorrelationId.Returns(CorrelationId);
            _fhirRequestContext.ResourceType.Returns("Patient");

            _fhirRequestContextAccessor.FhirRequestContext = _fhirRequestContext;

            _httpContext.Connection.RemoteIpAddress = CallerIpAddress;

            _claimsExtractor.Extract().Returns(Claims);

            _auditHelper = new AuditHelper(_fhirRequestContextAccessor, _auditLogger, _auditHeaderReader);
        }
        public async Task <CreateExportResponse> Handle(CreateExportRequest request, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(request, nameof(request));

            IReadOnlyCollection <KeyValuePair <string, string> > requestorClaims = _claimsExtractor.Extract()?
                                                                                   .OrderBy(claim => claim.Key, StringComparer.Ordinal).ToList();

            // Compute the hash of the job.
            var hashObject = new
            {
                request.RequestUri,
                RequestorClaims = requestorClaims,
                request.DestinationInfo,
            };

            string hash = JsonConvert.SerializeObject(hashObject).ComputeHash();

            // Check to see if a matching job exists or not. If a matching job exists, we will return that instead.
            // Otherwise, we will create a new export job. This will be a best effort since the likelihood of this happen should be small.
            ExportJobOutcome outcome = await _fhirOperationDataStore.GetExportJobByHashAsync(hash, cancellationToken);

            if (outcome == null)
            {
                // Remove the connection settings from the request URI before we store it in the secret store.
                NameValueCollection queryParameters = HttpUtility.ParseQueryString(request.RequestUri.Query);

                queryParameters.Remove(KnownQueryParameterNames.DestinationType);
                queryParameters.Remove(KnownQueryParameterNames.DestinationConnectionSettings);

                var uriBuilder = new UriBuilder(request.RequestUri);
                uriBuilder.Query = queryParameters.ToString();

                var jobRecord = new ExportJobRecord(uriBuilder.Uri, request.ResourceType, hash, requestorClaims);

                // Store the destination secret.
                try
                {
                    await _secretStore.SetSecretAsync(jobRecord.SecretName, request.DestinationInfo.ToJson(), cancellationToken);
                }
                catch (SecretStoreException sse)
                {
                    throw new OperationFailedException(string.Format(Resources.OperationFailed, OperationsConstants.Export, sse.Message), sse.ResponseStatusCode);
                }

                outcome = await _fhirOperationDataStore.CreateExportJobAsync(jobRecord, cancellationToken);
            }

            return(new CreateExportResponse(outcome.JobRecord.Id));
        }
Exemple #7
0
        public AuditHelperTests()
        {
            _fhirRequestContext.Uri.Returns(Uri);
            _fhirRequestContext.CorrelationId.Returns(CorrelationId);

            _fhirRequestContextAccessor.FhirRequestContext = _fhirRequestContext;

            _auditEventTypeMapping.GetAuditEventType(ControllerName, AnonymousActionName).Returns((string)null);
            _auditEventTypeMapping.GetAuditEventType(ControllerName, NonAnonymousActionName).Returns(AuditEventType);

            _httpContext.Connection.RemoteIpAddress = CallerIpAddress;

            _claimsExtractor.Extract().Returns(Claims);

            _auditHelper = new AuditHelper(_fhirRequestContextAccessor, _auditEventTypeMapping, _auditLogger, NullLogger <AuditHelper> .Instance, _auditHeaderReader);
        }
        /// <inheritdoc />
        public ResourceWrapper Create(ResourceElement resource, bool deleted, bool keepMeta)
        {
            RawResource rawResource = _rawResourceFactory.Create(resource, keepMeta);
            IReadOnlyCollection <SearchIndexEntry> searchIndices = _searchIndexer.Extract(resource);

            IFhirRequestContext fhirRequestContext = _fhirRequestContextAccessor.FhirRequestContext;

            return(new ResourceWrapper(
                       resource,
                       rawResource,
                       new ResourceRequest(fhirRequestContext.Method, fhirRequestContext.Uri),
                       deleted,
                       searchIndices,
                       _compartmentIndexer.Extract(resource.InstanceType, searchIndices),
                       _claimsExtractor.Extract()));
        }
Exemple #9
0
        public AuditHelperTests()
        {
            Type mockControllerType = typeof(MockController);

            var actionDescriptors = new List <ActionDescriptor>()
            {
                new ControllerActionDescriptor()
                {
                    ControllerName = ControllerName,
                    ActionName     = AnonymousMethodName,
                    MethodInfo     = mockControllerType.GetMethod(AnonymousMethodName),
                },
                new ControllerActionDescriptor()
                {
                    ControllerName = ControllerName,
                    ActionName     = AudittedMethodName,
                    MethodInfo     = mockControllerType.GetMethod(AudittedMethodName),
                },
                new ControllerActionDescriptor()
                {
                    ControllerName = ControllerName,
                    ActionName     = NoAttributeMethodName,
                    MethodInfo     = mockControllerType.GetMethod(NoAttributeMethodName),
                },
                new PageActionDescriptor()
                {
                },
            };

            var actionDescriptorCollection = new ActionDescriptorCollection(actionDescriptors, 1);

            _actionDescriptorCollectionProvider.ActionDescriptors.Returns(actionDescriptorCollection);

            _fhirRequestContext.Uri.Returns(Uri);
            _fhirRequestContext.CorrelationId.Returns(CorrelationId);

            _fhirRequestContextAccessor.FhirRequestContext = _fhirRequestContext;

            _httpContext.Connection.RemoteIpAddress = CallerIpAddress;

            _claimsExtractor.Extract().Returns(Claims);

            _auditHelper = new AuditHelper(_actionDescriptorCollectionProvider, _fhirRequestContextAccessor, _auditLogger, NullLogger <AuditHelper> .Instance);

            ((IStartable)_auditHelper).Start();
        }
        /// <inheritdoc />
        public ResourceWrapper Create(ResourceElement resource, bool deleted, bool keepMeta)
        {
            RawResource rawResource = _rawResourceFactory.Create(resource, keepMeta);
            IReadOnlyCollection <SearchIndexEntry> searchIndices = _searchIndexer.Extract(resource);
            string searchParamHash = _searchParameterDefinitionManager.GetSearchParameterHashForResourceType(resource.InstanceType);

            ExtractMinAndMaxValues(searchIndices);

            IFhirRequestContext fhirRequestContext = _fhirRequestContextAccessor.FhirRequestContext;

            return(new ResourceWrapper(
                       resource,
                       rawResource,
                       new ResourceRequest(fhirRequestContext.Method, fhirRequestContext.Uri),
                       deleted,
                       searchIndices,
                       _compartmentIndexer.Extract(resource.InstanceType, searchIndices),
                       _claimsExtractor.Extract(),
                       searchParamHash));
        }
Exemple #11
0
        private void Log(AuditAction auditAction, HttpStatusCode?statusCode, HttpContext httpContext, IClaimsExtractor claimsExtractor)
        {
            IRequestContext dicomRequestContext = _dicomRequestContextAccessor.DicomRequestContext;

            string auditEventType = dicomRequestContext.AuditEventType;

            // Audit the call if an audit event type is associated with the action.
            if (!string.IsNullOrEmpty(auditEventType))
            {
                _auditLogger.LogAudit(
                    auditAction,
                    operation: auditEventType,
                    requestUri: dicomRequestContext.Uri,
                    statusCode: statusCode,
                    correlationId: dicomRequestContext.CorrelationId,
                    callerIpAddress: httpContext.Connection?.RemoteIpAddress?.ToString(),
                    callerClaims: claimsExtractor.Extract(),
                    customHeaders: _auditHeaderReader.Read(httpContext));
            }
        }
Exemple #12
0
        private void Log(AuditAction auditAction, string controllerName, string actionName, HttpStatusCode?statusCode, string resourceType, HttpContext httpContext, IClaimsExtractor claimsExtractor)
        {
            string auditEventType = _auditEventTypeMapping.GetAuditEventType(controllerName, actionName);

            // Audit the call if an audit event type is associated with the action.
            if (auditEventType != null)
            {
                IFhirRequestContext fhirRequestContext = _fhirRequestContextAccessor.FhirRequestContext;

                _auditLogger.LogAudit(
                    auditAction,
                    operation: auditEventType,
                    resourceType: resourceType,
                    requestUri: fhirRequestContext.Uri,
                    statusCode: statusCode,
                    correlationId: fhirRequestContext.CorrelationId,
                    callerIpAddress: httpContext.Connection?.RemoteIpAddress?.ToString(),
                    callerClaims: claimsExtractor.Extract());
            }
        }
Exemple #13
0
        private void Log(AuditAction auditAction, string controllerName, string actionName, HttpStatusCode?statusCode, string resourceType, HttpContext httpContext, IClaimsExtractor claimsExtractor)
        {
            IFhirRequestContext fhirRequestContext = _fhirRequestContextAccessor.FhirRequestContext;

            // fhirRequestContext.AuditEventType will not be set in the case of an unauthorized call because the filter that sets it will not be executed
            string auditEventType = string.IsNullOrWhiteSpace(fhirRequestContext.AuditEventType) ? _auditEventTypeMapping.GetAuditEventType(controllerName, actionName) : fhirRequestContext.AuditEventType;

            // Audit the call if an audit event type is associated with the action.
            if (auditEventType != null)
            {
                _auditLogger.LogAudit(
                    auditAction,
                    operation: auditEventType,
                    resourceType: resourceType,
                    requestUri: fhirRequestContext.Uri,
                    statusCode: statusCode,
                    correlationId: fhirRequestContext.CorrelationId,
                    callerIpAddress: httpContext.Connection?.RemoteIpAddress?.ToString(),
                    callerClaims: claimsExtractor.Extract(),
                    customHeaders: _auditHeaderReader.Read(httpContext));
            }
        }
Exemple #14
0
        public async Task <CreateExportResponse> Handle(CreateExportRequest request, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(request, nameof(request));

            if (await _authorizationService.CheckAccess(DataActions.Export) != DataActions.Export)
            {
                throw new UnauthorizedFhirActionException();
            }

            IReadOnlyCollection <KeyValuePair <string, string> > requestorClaims = _claimsExtractor.Extract()?
                                                                                   .OrderBy(claim => claim.Key, StringComparer.Ordinal).ToList();

            // Compute the hash of the job.
            var hashObject = new
            {
                request.RequestUri,
                RequestorClaims = requestorClaims,
            };

            string hash = JsonConvert.SerializeObject(hashObject).ComputeHash();

            string storageAccountConnectionHash = string.IsNullOrEmpty(_exportJobConfiguration.StorageAccountConnection) ?
                                                  string.Empty :
                                                  Microsoft.Health.Core.Extensions.StringExtensions.ComputeHash(_exportJobConfiguration.StorageAccountConnection);

            // Check to see if a matching job exists or not. If a matching job exists, we will return that instead.
            // Otherwise, we will create a new export job. This will be a best effort since the likelihood of this happen should be small.
            ExportJobOutcome outcome = await _fhirOperationDataStore.GetExportJobByHashAsync(hash, cancellationToken);

            ExportJobFormatConfiguration formatConfiguration = null;

            if (request.FormatName != null)
            {
                formatConfiguration = _exportJobConfiguration.Formats?.FirstOrDefault(
                    (ExportJobFormatConfiguration formatConfig) => formatConfig.Name.Equals(request.FormatName, StringComparison.OrdinalIgnoreCase));

                if (formatConfiguration == null)
                {
                    throw new BadRequestException(Resources.ExportFormatNotFound);
                }
            }

            formatConfiguration ??= _exportJobConfiguration.Formats?.FirstOrDefault(
                (ExportJobFormatConfiguration formatConfig) => formatConfig.Default);

            formatConfiguration ??= new ExportJobFormatConfiguration()
            {
                Format = request.ContainerName == null ? ExportFormatTags.ResourceName : $"{ExportFormatTags.Timestamp}-{ExportFormatTags.Id}/{ExportFormatTags.ResourceName}",
            };

            if (outcome == null)
            {
                var jobRecord = new ExportJobRecord(
                    request.RequestUri,
                    request.RequestType,
                    formatConfiguration.Format,
                    request.ResourceType,
                    hash,
                    requestorClaims,
                    request.Since,
                    request.GroupId,
                    storageAccountConnectionHash,
                    _exportJobConfiguration.StorageAccountUri,
                    request.AnonymizationConfigurationLocation,
                    request.AnonymizationConfigurationFileETag,
                    _exportJobConfiguration.MaximumNumberOfResourcesPerQuery,
                    _exportJobConfiguration.NumberOfPagesPerCommit,
                    request.ContainerName);

                outcome = await _fhirOperationDataStore.CreateExportJobAsync(jobRecord, cancellationToken);
            }

            return(new CreateExportResponse(outcome.JobRecord.Id));
        }