예제 #1
0
        /// <inheritdoc />
        public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            if (next == null)
            {
                throw new ArgumentNullException(nameof(next));
            }

            // 1. Confirm this filter applies.
            var routeData = context.RouteData;

            if (!routeData.TryGetWebHookReceiverName(out var receiverName) || !IsApplicable(receiverName))
            {
                await next();

                return;
            }

            // 2. Confirm we were reached using HTTPS.
            var request     = context.HttpContext.Request;
            var errorResult = EnsureSecureConnection(receiverName, request);

            if (errorResult != null)
            {
                context.Result = errorResult;
                return;
            }

            // 3. Get XElement from the request body.
            var data = await _requestReader.ReadBodyAsync <XElement>(context);

            if (data == 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;
            }

            // 4. Ensure the organization ID exists and matches the expected value.
            var organizationIds = ObjectPathUtilities.GetStringValues(data, SalesforceConstants.OrganizationIdPath);

            if (StringValues.IsNullOrEmpty(organizationIds))
            {
                Logger.LogError(
                    0,
                    "The HTTP request body did not contain a required '{XPath}' element.",
                    SalesforceConstants.OrganizationIdPath);

                var message = string.Format(
                    CultureInfo.CurrentCulture,
                    Resources.VerifyOrganization_MissingValue,
                    SalesforceConstants.OrganizationIdPath);
                context.Result = await _resultCreator.GetFailedResultAsync(message);

                return;
            }

            var secret = GetSecretKey(
                ReceiverName,
                routeData,
                SalesforceConstants.SecretKeyMinLength,
                SalesforceConstants.SecretKeyMaxLength);

            var organizationId = GetShortOrganizationId(organizationIds[0]);
            var secretKey      = GetShortOrganizationId(secret);

            if (!SecretEqual(organizationId, secretKey))
            {
                Logger.LogError(
                    1,
                    "The '{XPath}' value provided in the HTTP request body did not match the expected value.",
                    SalesforceConstants.OrganizationIdPath);

                var message = string.Format(
                    CultureInfo.CurrentCulture,
                    Resources.VerifyOrganization_BadValue,
                    SalesforceConstants.OrganizationIdPath);
                context.Result = await _resultCreator.GetFailedResultAsync(message);

                return;
            }

            // 5. Get the event name.
            var eventNames = ObjectPathUtilities.GetStringValues(data, SalesforceConstants.EventNamePath);

            if (StringValues.IsNullOrEmpty(eventNames))
            {
                Logger.LogError(
                    2,
                    "The HTTP request body did not contain a required '{XPath}' element.",
                    SalesforceConstants.EventNamePath);

                var message = string.Format(
                    CultureInfo.CurrentCulture,
                    Resources.VerifyOrganization_MissingValue,
                    SalesforceConstants.EventNamePath);
                context.Result = await _resultCreator.GetFailedResultAsync(message);

                return;
            }

            // 6. Success. Provide event name for model binding.
            routeData.SetWebHookEventNames(eventNames);

            await next();
        }
예제 #2
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;

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

                return;
            }

            var eventFromBodyMetadata = _eventFromBodyMetadata
                                        .FirstOrDefault(metadata => metadata.IsApplicable(receiverName));

            if (eventFromBodyMetadata == null)
            {
                await next();

                return;
            }

            // Determine the applicable WebhookBodyType i.e. how to read the request body.
            // WebHookReceiverExistsConstraint confirms the IWebHookBodyTypeMetadataService implementation exists.
            var bodyTypeMetadata = _bodyTypeMetadata.First(metadata => metadata.IsApplicable(receiverName));

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