Beispiel #1
0
        public AuditLoggingFilterTests()
        {
            _fhirResult = new FhirResult(new Patient()
            {
                Name = { new HumanName()
                         {
                             Text = "TestPatient"
                         } }
            });

            _executedContext = new ResultExecutedContext(
                new ActionContext(new DefaultHttpContext(), new RouteData(), new ControllerActionDescriptor()
            {
                DisplayName = "Executed Context Test Descriptor"
            }),
                new List <IFilterMetadata>(),
                _fhirResult,
                FilterTestsHelper.CreateMockFhirController());

            _executedContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Created;
            _fhirResult.StatusCode = HttpStatusCode.Created;
            _fhirRequestContext.RequestType.Returns(new Coding("System", "TestRequestType"));
            _fhirRequestContext.RequestSubType = new Coding("System", "TestRequestSubType");
            _fhirRequestContext.Uri.Returns(new Uri("https://fhirtest/fhir?count=100"));
            _fhirRequestContextAccessor.FhirRequestContext.Returns(_fhirRequestContext);
            _fhirRequestContextAccessor.FhirRequestContext.Principal.Returns(_claimsPrincipal);

            _securityConfiguration.LastModifiedClaims.Returns(new HashSet <string> {
                "claim1"
            });
            _securityOptions.Value.Returns(_securityConfiguration);
            _claimsPrincipal.Claims.Returns(new List <System.Security.Claims.Claim> {
                Claim1
            });

            _claims = new KeyValuePair <string, string>[]
            {
                KeyValuePair.Create("claim", "value"),
            };

            _claimsIndexer = Substitute.For <IClaimsIndexer>();

            _claimsIndexer.Extract().Returns(_claims);

            _filter = new AuditLoggingFilterAttribute(
                _auditLogger,
                _fhirRequestContextAccessor,
                _claimsIndexer);
        }
        public IActionResult CustomError(int?statusCode = null)
        {
            HttpStatusCode returnCode;

            OperationOutcome.IssueType issueType;
            string diagnosticInfo;

            switch (statusCode)
            {
            case (int)HttpStatusCode.Unauthorized:
                issueType      = OperationOutcome.IssueType.Login;
                returnCode     = HttpStatusCode.Unauthorized;
                diagnosticInfo = Resources.Unauthorized;
                break;

            case (int)HttpStatusCode.Forbidden:
                issueType      = OperationOutcome.IssueType.Forbidden;
                returnCode     = HttpStatusCode.Forbidden;
                diagnosticInfo = Resources.Forbidden;
                break;

            case (int)HttpStatusCode.NotFound:
                issueType      = OperationOutcome.IssueType.NotFound;
                returnCode     = HttpStatusCode.NotFound;
                diagnosticInfo = Resources.NotFoundException;
                break;

            default:
                issueType      = OperationOutcome.IssueType.Exception;
                returnCode     = HttpStatusCode.InternalServerError;
                diagnosticInfo = Resources.GeneralInternalError;
                break;
            }

            return(FhirResult.Create(
                       new OperationOutcome
            {
                Id = _fhirRequestContextAccessor.FhirRequestContext.CorrelationId,
                Issue = new List <OperationOutcome.IssueComponent>
                {
                    new OperationOutcome.IssueComponent
                    {
                        Severity = OperationOutcome.IssueSeverity.Error,
                        Code = issueType,
                        Diagnostics = diagnosticInfo,
                    },
                },
            }.ToResourceElement(), returnCode));
        }
        public async Task <IActionResult> CreateReindexJob([FromBody] Parameters inputParams)
        {
            CheckIfReindexIsEnabledAndRespond();

            ValidateParams(inputParams);

            ushort?maximumConcurrency = ReadNumericParameter(inputParams, JobRecordProperties.MaximumConcurrency);
            string scope = ReadStringParameter(inputParams, JobRecordProperties.Scope);

            ResourceElement response = await _mediator.CreateReindexJobAsync(maximumConcurrency, scope, HttpContext.RequestAborted);

            return(FhirResult.Create(response, HttpStatusCode.Created)
                   .SetETagHeader()
                   .SetLastModifiedHeader());
        }
Beispiel #4
0
        public static FhirResult SetLocationHeader(this FhirResult fhirResult, IUrlResolver urlResolver)
        {
            var resource = fhirResult.Resource;

            if (!string.IsNullOrEmpty(resource.Id) && !string.IsNullOrWhiteSpace(resource.Meta.VersionId))
            {
                var url = urlResolver.ResolveResourceUrl(resource, true);

                if (url.IsAbsoluteUri)
                {
                    fhirResult.Headers.Add(HeaderNames.Location, url.AbsoluteUri);
                }
            }

            return(fhirResult);
        }
        public void GivenAController_WhenExecutedAction_ThenAuditLogShouldBeLogged()
        {
            const HttpStatusCode expectedStatusCode = HttpStatusCode.Created;

            var fhirResult = new FhirResult(new Patient()
            {
                Name = { new HumanName()
                         {
                             Text = "TestPatient"
                         } }
            });

            SetupExecutedAction(expectedStatusCode, fhirResult);

            _auditHelper.Received(1).LogExecuted(ControllerName, ActionName, expectedStatusCode, "Patient");
        }
Beispiel #6
0
        public async Task <IActionResult> CreateReindexJob([FromBody] Parameters inputParams)
        {
            CheckIfReindexIsEnabledAndRespond();

            ValidateParams(inputParams);

            ushort?maximumConcurrency = ReadNumericParameter(inputParams, JobRecordProperties.MaximumConcurrency);

            ResourceElement response = await _mediator.CreateReindexJobAsync(maximumConcurrency, HttpContext.RequestAborted);

            var result = FhirResult.Create(response, HttpStatusCode.Created)
                         .SetETagHeader()
                         .SetLastModifiedHeader();

            result.SetContentLocationHeader(_urlResolver, OperationsConstants.Reindex, response.Id);
            return(result);
        }
Beispiel #7
0
        private IActionResult ToSaveOutcomeResult(SaveOutcome saveOutcome)
        {
            switch (saveOutcome.Outcome)
            {
                case SaveOutcomeType.Created:
                    return FhirResult.Create(saveOutcome.RawResourceElement, HttpStatusCode.Created)
                        .SetETagHeader()
                        .SetLastModifiedHeader()
                        .SetLocationHeader(_urlResolver);
                case SaveOutcomeType.Updated:
                    return FhirResult.Create(saveOutcome.RawResourceElement, HttpStatusCode.OK)
                        .SetETagHeader()
                        .SetLastModifiedHeader();
            }

            return FhirResult.Create(saveOutcome.RawResourceElement, HttpStatusCode.BadRequest);
        }
Beispiel #8
0
        public async Task <IActionResult> Delete(string typeParameter, string idParameter, [FromQuery] bool hardDelete)
        {
            string policy = PolicyNames.WritePolicy;

            if (hardDelete)
            {
                policy = PolicyNames.HardDeletePolicy;
            }

            AuthorizationResult authorizationResult = await _authorizationService.AuthorizeAsync(User, policy);

            if (!authorizationResult.Succeeded)
            {
                return(Forbid());
            }

            DeleteResourceResponse response = await _mediator.DeleteResourceAsync(new ResourceKey(typeParameter, idParameter), hardDelete, HttpContext.RequestAborted);

            return(FhirResult.NoContent().SetETagHeader(response.WeakETag));
        }
        public async Task <IActionResult> ConditionalCreate([FromBody] Resource resource)
        {
            StringValues conditionalCreateHeader = HttpContext.Request.Headers[KnownFhirHeaders.IfNoneExist];

            Tuple <string, string>[] conditionalParameters = QueryHelpers.ParseQuery(conditionalCreateHeader)
                                                             .SelectMany(query => query.Value, (query, value) => Tuple.Create(query.Key, value)).ToArray();

            UpsertResourceResponse createResponse = await _mediator.Send <UpsertResourceResponse>(new ConditionalCreateResourceRequest(resource.ToResourceElement(), conditionalParameters), HttpContext.RequestAborted);

            if (createResponse == null)
            {
                return(Ok());
            }

            ResourceElement response = createResponse.Outcome.Resource;

            return(FhirResult.Create(response, HttpStatusCode.Created)
                   .SetETagHeader()
                   .SetLastModifiedHeader()
                   .SetLocationHeader(_urlResolver));
        }
        public void GivenAController_WhenExecutedAction_ThenAuditLogShouldBeLogged()
        {
            var fhirResult = new FhirResult(new Patient()
            {
                Name = { new HumanName()
                         {
                             Text = "TestPatient"
                         } }
            }.ToResourceElement());

            var resultExecutedContext = new ResultExecutedContext(
                new ActionContext(_httpContext, new RouteData(), new ControllerActionDescriptor()
            {
                DisplayName = "Executed Context Test Descriptor"
            }),
                new List <IFilterMetadata>(),
                fhirResult,
                FilterTestsHelper.CreateMockFhirController());

            _filter.OnResultExecuted(resultExecutedContext);

            _auditHelper.Received(1).LogExecuted(_httpContext, _claimsExtractor);
        }
Beispiel #11
0
        public void WhenSettingALastModifiedHeader_ThenFhirResultHasALastModifierHeader()
        {
            var fhirResult = FhirResult.Create(_mockResource).SetLastModifiedHeader();

            Assert.Equal(_mockResource.LastUpdated?.ToString("r", CultureInfo.InvariantCulture), fhirResult.Headers[HeaderNames.LastModified]);
        }
Beispiel #12
0
        public async Task <IActionResult> Metadata(bool system = false)
        {
            ResourceElement response = await _mediator.GetCapabilitiesAsync(system, HttpContext.RequestAborted);

            return(FhirResult.Create(response));
        }
Beispiel #13
0
        private async Task <IActionResult> PerformSearch(string type, IReadOnlyList <Tuple <string, string> > queries)
        {
            ResourceElement response = await _mediator.SearchResourceAsync(type, queries, HttpContext.RequestAborted);

            return(FhirResult.Create(response));
        }
Beispiel #14
0
        public async Task <IActionResult> BatchAndTransactions([FromBody] Resource bundle)
        {
            ResourceElement bundleResponse = await _mediator.PostBundle(bundle.ToResourceElement());

            return(FhirResult.Create(bundleResponse));
        }
Beispiel #15
0
        public async Task <IActionResult> Delete(string typeParameter, string idParameter, [FromQuery] bool hardDelete)
        {
            DeleteResourceResponse response = await _mediator.DeleteResourceAsync(new ResourceKey(typeParameter, idParameter), hardDelete, HttpContext.RequestAborted);

            return(FhirResult.NoContent().SetETagHeader(response.WeakETag));
        }
Beispiel #16
0
        private async Task <IActionResult> PerformSearch(string type, IReadOnlyList <Tuple <string, string> > queries)
        {
            var response = await _mediator.Send(new SearchResourceRequest(type, queries), HttpContext.RequestAborted);

            return(FhirResult.Create(response.Bundle));
        }
Beispiel #17
0
        private async Task<IActionResult> PerformCompartmentSearch(string compartmentType, string compartmentId, string resourceType, IReadOnlyList<Tuple<string, string>> queries)
        {
            ResourceElement response = await _mediator.SearchResourceCompartmentAsync(compartmentType, compartmentId, resourceType, queries, HttpContext.RequestAborted);

            return FhirResult.Create(response);
        }
Beispiel #18
0
 public void WhenAddingSameHeaderTwice_ThenOnlyOneHeaderIsPresent()
 {
     Assert.Throws <ArgumentException>(() => FhirResult.Create(_mockResource)
                                       .SetLastModifiedHeader()
                                       .SetLastModifiedHeader());
 }
Beispiel #19
0
        public void WhenCreatingAFhirResultAndNotSettingHeaders_ThenThereIsNoHeaders()
        {
            var fhirResult = FhirResult.Create(_mockResource);

            Assert.Empty(fhirResult.Headers);
        }
Beispiel #20
0
        public async Task Invoke(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (Exception exception)
            {
                if (context.Response.HasStarted)
                {
                    _logger.LogWarning("The response has already started, the base exception middleware will not be executed.");
                    throw;
                }

                var localCorrelationId = _fhirRequestContextAccessor.FhirRequestContext?.CorrelationId;

                if (string.IsNullOrWhiteSpace(localCorrelationId))
                {
                    localCorrelationId = _correlationIdProvider.Invoke();
                    _logger.LogError($"No correlation id available in exception middleware. Setting to {localCorrelationId}");
                }

                context.Response.Clear();

                var diagnostics = Resources.GeneralInternalError;

                // If any of these exceptions are encountered, show a more specific diagnostic message
                if (exception.Message.StartsWith("IDX10803: Unable to obtain configuration from:", StringComparison.OrdinalIgnoreCase))
                {
                    diagnostics = Resources.UnableToObtainOpenIdConfiguration;
                }
                else if (exception.Message.StartsWith("The MetadataAddress or Authority must use HTTPS", StringComparison.OrdinalIgnoreCase))
                {
                    diagnostics = Resources.RequireHttpsMetadataError;
                }

                var operationOutcome = new OperationOutcome
                {
                    Id    = localCorrelationId,
                    Issue = new List <OperationOutcome.IssueComponent>
                    {
                        new OperationOutcome.IssueComponent
                        {
                            Severity    = OperationOutcome.IssueSeverity.Fatal,
                            Code        = OperationOutcome.IssueType.Exception,
                            Diagnostics = diagnostics,
                        },
                    },
                };

                try
                {
                    await _contentTypeService.CheckRequestedContentTypeAsync(context);
                }
                catch (UnsupportedMediaTypeException)
                {
                    context.Response.ContentType = ContentType.JSON_CONTENT_HEADER;
                }

                var result = FhirResult.Create(operationOutcome, HttpStatusCode.InternalServerError);

                await ExecuteResultAsync(context, result);
            }
        }
Beispiel #21
0
        public void WhenAddingStringEtag_ThenStringETagIsReturned()
        {
            var fhirResult = FhirResult.Create(_mockResource).SetETagHeader("etag");

            Assert.Equal("etag", fhirResult.Headers[HeaderNames.ETag]);
        }
Beispiel #22
0
        public async Task <IActionResult> PurgeHistory(string typeParameter, string idParameter)
        {
            DeleteResourceResponse response = await _mediator.DeleteResourceAsync(new ResourceKey(typeParameter, idParameter), DeleteOperation.PurgeHistory, HttpContext.RequestAborted);

            return(FhirResult.NoContent().SetETagHeader(response.WeakETag));
        }
Beispiel #23
0
        public void WhenAddingTwoHeaders_ThenFhirResultHasAtLeastTwoHeaders()
        {
            var fhirResult = FhirResult.Create(_mockResource).SetLastModifiedHeader().SetETagHeader();

            Assert.Equal(2, fhirResult.Headers.Count);
        }
Beispiel #24
0
        public override void OnActionExecuted(ActionExecutedContext context)
        {
            EnsureArg.IsNotNull(context, nameof(context));

            if (context?.Exception is FhirException fhirException)
            {
                FhirResult fhirResult = FhirResult.Create(
                    new OperationOutcome
                {
                    Id    = _fhirRequestContextAccessor.FhirRequestContext.CorrelationId,
                    Issue = fhirException.Issues.ToList(),
                }, HttpStatusCode.BadRequest);

                switch (fhirException)
                {
                case ResourceGoneException resourceGoneException:
                    fhirResult.StatusCode = HttpStatusCode.Gone;
                    if (!string.IsNullOrEmpty(resourceGoneException.DeletedResource?.VersionId))
                    {
                        fhirResult.SetETagHeader(WeakETag.FromVersionId(resourceGoneException.DeletedResource.VersionId));
                    }

                    break;

                case ResourceNotFoundException _:
                    fhirResult.StatusCode = HttpStatusCode.NotFound;
                    break;

                case MethodNotAllowedException _:
                    fhirResult.StatusCode = HttpStatusCode.MethodNotAllowed;
                    break;

                case ServiceUnavailableException _:
                case OpenIdConfigurationException _:
                    fhirResult.StatusCode = HttpStatusCode.ServiceUnavailable;
                    break;

                case ResourceNotValidException _:
                case BadRequestException _:
                    fhirResult.StatusCode = HttpStatusCode.BadRequest;
                    break;

                case ResourceConflictException _:
                    fhirResult.StatusCode = HttpStatusCode.Conflict;
                    break;

                case UnsupportedMediaTypeException _:
                    fhirResult.StatusCode = HttpStatusCode.UnsupportedMediaType;
                    break;

                case PreconditionFailedException _:
                    fhirResult.StatusCode = HttpStatusCode.PreconditionFailed;
                    break;

                case InvalidSearchOperationException _:
                case SearchOperationNotSupportedException _:
                    fhirResult.StatusCode = HttpStatusCode.Forbidden;
                    break;

                case UnsupportedConfigurationException _:
                    fhirResult.StatusCode = HttpStatusCode.InternalServerError;
                    break;

                case RequestRateExceededException ex:
                    fhirResult.StatusCode = HttpStatusCode.TooManyRequests;

                    if (ex.RetryAfter != null)
                    {
                        fhirResult.Headers.Add(
                            RetryAfterHeaderName,
                            ex.RetryAfter.Value.TotalMilliseconds.ToString(CultureInfo.InvariantCulture));
                    }

                    break;
                }

                context.Result           = fhirResult;
                context.ExceptionHandled = true;
            }
        }
Beispiel #25
0
        public void WhenAddingStringEtag_ThenStringETagIsReturned()
        {
            var fhirResult = FhirResult.Create(_mockResource).SetETagHeader(WeakETag.FromVersionId("etag"));

            Assert.Equal("W/\"etag\"", fhirResult.Headers[HeaderNames.ETag]);
        }
Beispiel #26
0
        private async Task <IActionResult> PerformCompartmentSearch(string compartmentType, string compartmentId, string resourceType, IReadOnlyList <Tuple <string, string> > queries)
        {
            var response = await _mediator.Send(new CompartmentResourceRequest(compartmentType, compartmentId, resourceType, queries), HttpContext.RequestAborted);

            return(FhirResult.Create(response.Bundle));
        }