Exemplo n.º 1
0
        /// <inheritdoc />
        public bool Accept(ActionConstraintContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (!context.RouteContext.RouteData.TryGetWebHookReceiverName(
                    context.CurrentCandidate.Action,
                    out var receiverName))
            {
                return(false);
            }

            if (_bodyTypeMetadata == null)
            {
                if (_metadataProvider.GetBodyTypeMetadata(receiverName) == null)
                {
                    // Received a request for (say) https://{host}/api/webhooks/incoming/mine but the "mine" receiver
                    // is not configured. But, probably not a misconfiguration in this application.
                    // WebHookMetadataProvier detects "incomplete" receivers i.e. those with some metadata
                    // services but lacking an IWebHookBodyTypeMetadataService implementation.
                    return(false);
                }
            }
            else
            {
                if (!_bodyTypeMetadata.IsApplicable(receiverName))
                {
                    // Received a request for (say) https://{host}/api/webhooks/incoming/their but this action is
                    // configured for the "mine" receiver.
                    return(false);
                }
            }

            context.RouteContext.RouteData.Values[WebHookConstants.ReceiverExistsKeyName] = true;
            return(true);
        }
        /// <inheritdoc />
        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            // bodyTypeMetadata will never end up null. WebHookActionModelPropertyProvider and
            // WebHookEventNameConstraint confirms the IWebHookBodyTypeMetadataService implementation exists.
            var bodyTypeMetadata = _bodyTypeMetadata;

            if (bodyTypeMetadata == null)
            {
                if (!context.RouteData.TryGetWebHookReceiverName(out var requestReceiverName))
                {
                    return;
                }

                bodyTypeMetadata = _metadataProvider.GetBodyTypeMetadata(requestReceiverName);
            }

            var receiverName = bodyTypeMetadata.ReceiverName;
            var request      = context.HttpContext.Request;
            var contentType  = request.GetTypedHeaders().ContentType;

            switch (bodyTypeMetadata.BodyType)
            {
            case WebHookBodyType.Form:
                if (!request.HasFormContentType)
                {
                    _logger.LogWarning(
                        0,
                        "The '{ReceiverName}' WebHook receiver does not support content type '{ContentType}'. " +
                        "The WebHook request must contain an entity body formatted as HTML form URL-encoded data.",
                        receiverName,
                        contentType);
                    var message = string.Format(
                        CultureInfo.CurrentCulture,
                        Resources.VerifyBody_NoFormData,
                        receiverName,
                        contentType);
                    context.Result = new BadRequestObjectResult(message)
                    {
                        StatusCode = StatusCodes.Status415UnsupportedMediaType
                    };
                }
                break;

            case WebHookBodyType.Json:
                if (!IsJson(contentType))
                {
                    _logger.LogWarning(
                        1,
                        "The '{ReceiverName}' WebHook receiver does not support content type '{ContentType}'. " +
                        "The WebHook request must contain an entity body formatted as JSON.",
                        receiverName,
                        contentType);
                    var message = string.Format(
                        CultureInfo.CurrentCulture,
                        Resources.VerifyBody_NoJson,
                        receiverName,
                        contentType);
                    context.Result = new BadRequestObjectResult(message)
                    {
                        StatusCode = StatusCodes.Status415UnsupportedMediaType
                    };
                }
                break;

            case WebHookBodyType.Xml:
                if (!IsXml(contentType))
                {
                    _logger.LogWarning(
                        2,
                        "The '{ReceiverName}' WebHook receiver does not support content type '{ContentType}'. " +
                        "The WebHook request must contain an entity body formatted as XML.",
                        receiverName,
                        contentType);
                    var message = string.Format(
                        CultureInfo.CurrentCulture,
                        Resources.VerifyBody_NoXml,
                        receiverName,
                        contentType);
                    context.Result = new BadRequestObjectResult(message)
                    {
                        StatusCode = StatusCodes.Status415UnsupportedMediaType
                    };
                }
                break;

            default:
            {
                var message = string.Format(
                    CultureInfo.CurrentCulture,
                    Resources.General_InvalidEnumValue,
                    typeof(WebHookBodyType),
                    bodyTypeMetadata.BodyType);
                throw new InvalidOperationException(message);
            }
            }
        }
Exemplo n.º 3
0
        /// <inheritdoc />
        public virtual async Task OnResourceExecutionAsync(
            ResourceExecutingContext context,
            ResourceExecutionDelegate next)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            if (next == null)
            {
                throw new ArgumentNullException(nameof(next));
            }

            var routeData             = context.RouteData;
            var bodyTypeMetadata      = _bodyTypeMetadata;
            var eventFromBodyMetadata = _eventFromBodyMetadata;

            if (bodyTypeMetadata == null)
            {
                if (!routeData.TryGetWebHookReceiverName(out var receiverName))
                {
                    await next();

                    return;
                }

                bodyTypeMetadata      = _metadataProvider.GetBodyTypeMetadata(receiverName);
                eventFromBodyMetadata = _metadataProvider.GetEventFromBodyMetadata(receiverName);
                if (eventFromBodyMetadata == null)
                {
                    await next();

                    return;
                }
            }

            // No need to double-check the request's Content-Type. WebHookVerifyBodyTypeFilter would have
            // short-circuited the request if unsupported.
            StringValues eventNames;

            switch (bodyTypeMetadata.BodyType)
            {
            case WebHookBodyType.Form:
                var form = await _requestReader.ReadAsFormDataAsync(context);

                if (form == null)
                {
                    // ReadAsFormDataAsync returns null only when other filters will log and return errors
                    // about the same conditions. Let those filters run.
                    await next();

                    return;
                }

                eventNames = form[eventFromBodyMetadata.BodyPropertyPath];
                break;

            case WebHookBodyType.Json:
                var json = await _requestReader.ReadBodyAsync <JContainer>(context);

                if (json == null)
                {
                    var modelState = context.ModelState;
                    if (modelState.IsValid)
                    {
                        // ReadAsJContainerAsync returns null when model state is valid only when other filters
                        // will log and return errors about the same conditions. Let those filters run.
                        await next();
                    }
                    else
                    {
                        context.Result = new BadRequestObjectResult(modelState);
                    }

                    return;
                }

                eventNames = ObjectPathUtilities.GetStringValues(json, eventFromBodyMetadata.BodyPropertyPath);
                break;

            case WebHookBodyType.Xml:
                var xml = await _requestReader.ReadBodyAsync <XElement>(context);

                if (xml == null)
                {
                    var modelState = context.ModelState;
                    if (modelState.IsValid)
                    {
                        // ReadAsXmlAsync returns null when model state is valid only when other filters will log
                        // and return errors about the same conditions. Let those filters run.
                        await next();
                    }
                    else
                    {
                        context.Result = new BadRequestObjectResult(modelState);
                    }

                    return;
                }

                eventNames = ObjectPathUtilities.GetStringValues(xml, eventFromBodyMetadata.BodyPropertyPath);
                break;

            default:
                var message = string.Format(
                    CultureInfo.CurrentCulture,
                    Resources.General_InvalidEnumValue,
                    typeof(WebHookBodyType),
                    bodyTypeMetadata.BodyType);
                throw new InvalidOperationException(message);
            }

            if (StringValues.IsNullOrEmpty(eventNames) && !eventFromBodyMetadata.AllowMissing)
            {
                var receiverName = bodyTypeMetadata.ReceiverName;
                _logger.LogWarning(
                    0,
                    "A '{ReceiverName}' WebHook request must contain a match for '{BodyPropertyPath}' in the HTTP " +
                    "request entity body indicating the type or types of event.",
                    receiverName,
                    eventFromBodyMetadata.BodyPropertyPath);

                var message = string.Format(
                    CultureInfo.CurrentCulture,
                    Resources.EventMapper_NoBodyProperty,
                    receiverName,
                    eventFromBodyMetadata.BodyPropertyPath);
                context.Result = new BadRequestObjectResult(message);

                return;
            }

            routeData.SetWebHookEventNames(eventNames);

            await next();
        }