public async Task <IActionResult> Receive([FromBody] ApplicationPayload payload)
        {
            // Find handler
            if (!RouteData.Values.TryGetValue(RouteKeyConstants.HandlerType, out object t) ||
                !(t is Type handlerType) ||
                !(_serviceProvider.GetService(handlerType) is ISpaceWebHookHandler handler))
            {
                return(BadRequest($"No registered {nameof(ISpaceWebHookHandler)} could be found."));
            }

            // Handle payload
            switch (payload)
            {
            // List commands?
            case ListCommandsPayload listCommandsPayload:
                var commands = await handler.HandleListCommandsAsync(listCommandsPayload);

                PropagatePropertyAccessPathHelper.SetAccessPathForValue(string.Empty, false, commands);

                // ReSharper disable once ConstantNullCoalescingCondition
                commands.CommandsItems ??= new List <CommandDetail>();

                return(new JsonResult(commands, JsonSerializerOptions));

            // List menu extensions?
            case ListMenuExtensionsPayload listMenuExtensionsPayload:
                var menuExtensions = await handler.HandleListMenuExtensionsAsync(listMenuExtensionsPayload);

                PropagatePropertyAccessPathHelper.SetAccessPathForValue(string.Empty, false, menuExtensions);

                // ReSharper disable once ConstantNullCoalescingCondition
                menuExtensions.Extensions ??= new List <MenuExtensionDetail>();

                return(new JsonResult(menuExtensions, JsonSerializerOptions));

            // Message?
            case MessagePayload messagePayload:
                await handler.HandleMessageAsync(messagePayload);

                return(Ok());

            // Action?
            case MessageActionPayload actionPayload:
                var actionResult = await handler.HandleMessageActionAsync(actionPayload);

                return(!string.IsNullOrEmpty(actionResult.Message)
                        ? Content(actionResult.Message, "text/plain") as IActionResult
                        : Ok());

            // Menu action?
            case MenuActionPayload menuActionPayload:
                var menuActionResult = await handler.HandleMenuActionAsync(menuActionPayload);

                return(!string.IsNullOrEmpty(menuActionResult.Message)
                        ? Content(menuActionResult.Message, "text/plain") as IActionResult
                        : Ok());
            }

            return(BadRequest("Payload is not supported."));
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Sends a payload to a resource at a given URL.
        /// </summary>
        /// <param name="httpMethod">The HTTP method to use.</param>
        /// <param name="urlPath">The path to access the resource.</param>
        /// <param name="payload">The payload to send to the resource.</param>
        /// <param name="cancellationToken">The cancellation token to cancel operation.</param>
        /// <returns>The requested resource.</returns>
        /// <exception cref="ResourceException">Something went wrong accessing the resource.</exception>
        public async Task <TResult> RequestResourceAsync <TPayload, TResult>(string httpMethod, string urlPath, TPayload payload, CancellationToken cancellationToken = default)
        {
            var value = await RequestResourceInternalAsync <TPayload, TResult>(httpMethod, urlPath, payload, cancellationToken);

            PropagatePropertyAccessPathHelper.SetAccessPathForValue(typeof(TResult).Name, true, value);

            return(value);
        }
        public override IClassNameConvertible Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            if (reader.TokenType == JsonTokenType.Null)
            {
                return(null);
            }

            var readerAtStart = reader;

            using var jsonDocument = JsonDocument.ParseValue(ref reader);
            var jsonObject = jsonDocument.RootElement;

            var className = jsonObject.GetStringValue("className");

            if (!string.IsNullOrEmpty(className))
            {
                if (!TypeMap.TryGetValue(className, out var targetType))
                {
                    targetType = Type.GetType(SpaceDotNetClientNamespace + "." + CSharpIdentifier.ForClassOrNamespace(className) + ", " + SpaceDotNetClientAssemblyName);
                    if (targetType != null)
                    {
                        TypeMap[className] = targetType;
                    }
                }
                if (targetType != null && typeof(IClassNameConvertible).IsAssignableFrom(targetType))
                {
                    var value = JsonSerializer.Deserialize(ref readerAtStart, targetType, options) as IClassNameConvertible;

                    PropagatePropertyAccessPathHelper.SetAccessPathForValue(targetType.Name, true, value);

                    return(value);
                }
            }

            return(null);
        }
        /// <inheritdoc />
        public sealed override async Task <InputFormatterResult> ReadRequestBodyAsync(
            InputFormatterContext context,
            Encoding encoding)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (encoding == null)
            {
                throw new ArgumentNullException(nameof(encoding));
            }

            var httpContext = context.HttpContext;
            var inputStream = httpContext.Request.Body;

            ApplicationPayload?model;

            try
            {
                using var inputStreamReader = new StreamReader(inputStream);

                var inputJsonString = await inputStreamReader.ReadToEndAsync();

                // Determine options name to use (in case multiple are registered)
                var options = httpContext.Request.RouteValues.TryGetValue(RouteKeyConstants.OptionsName, out object optionsName)
                        ? _options.Get(optionsName.ToString())
                        : _options.CurrentValue;

                // Verify signature
                if (options.ValidatePayloadSignature)
                {
                    if (!string.IsNullOrEmpty(options.EndpointSigningKey))
                    {
                        var secret = Encoding.ASCII.GetBytes(options.EndpointSigningKey);

                        var signatureBytes = Encoding.UTF8.GetBytes(context.HttpContext.Request.Headers[HeaderSpaceTimestamp] + ":" + inputJsonString);
                        using var hmSha1 = new HMACSHA256(secret);
                        var signatureHash   = hmSha1.ComputeHash(signatureBytes);
                        var signatureString = ToHexString(signatureHash);
                        if (!signatureString.Equals(context.HttpContext.Request.Headers[HeaderSpaceSignature]))
                        {
                            throw new InvalidOperationException("The webhook signature does not match the webhook payload. Make sure the endpoint signing key is configured correctly in your Space organization, and the current application.");
                        }
                    }
                    else
                    {
                        _logger.LogWarning(nameof(SpaceWebHookOptions.ValidatePayloadSignature) + " is enabled, but no " + nameof(SpaceWebHookOptions.EndpointSigningKey) + " is configured. Skipping payload signature validation.");
                    }
                }

                // Deserialize model
                model = JsonSerializer.Deserialize(inputJsonString, context.ModelType, _jsonSerializerOptions) as ApplicationPayload;
                if (model != null)
                {
                    PropagatePropertyAccessPathHelper.SetAccessPathForValue(string.Empty, false, model);
                }

                // Verify payload
                if (options.ValidatePayloadVerificationToken)
                {
                    var payloadVerificationTokenValue = GetPayloadVerificationTokenValue(model);
                    if (!string.IsNullOrEmpty(payloadVerificationTokenValue))
                    {
                        if (payloadVerificationTokenValue != options.EndpointVerificationToken)
                        {
                            throw new InvalidOperationException(
                                      "The webhook verification token does not your configured verification token. Make sure the endpoint verification token is configured correctly in your Space organization, and the current application.");
                        }
                    }
                    else
                    {
                        _logger.LogWarning(nameof(SpaceWebHookOptions.ValidatePayloadVerificationToken) + " is enabled, but no " + nameof(SpaceWebHookOptions.EndpointVerificationToken) + " is configured. Skipping verification token validation.");
                    }
                }
            }
            catch (JsonException jsonException)
            {
                var path = jsonException.Path;

                var formatterException = new InputFormatterException(jsonException.Message, jsonException);
                context.ModelState.TryAddModelError(path, formatterException, context.Metadata);

                Log.JsonInputException(_logger, jsonException);

                return(await InputFormatterResult.FailureAsync());
            }
            catch (Exception exception) when(exception is FormatException || exception is OverflowException)
            {
                context.ModelState.TryAddModelError(string.Empty, exception, context.Metadata);

                Log.JsonInputException(_logger, exception);

                return(await InputFormatterResult.FailureAsync());
            }

            if (model == null && !context.TreatEmptyInputAsDefaultValue)
            {
                return(await InputFormatterResult.NoValueAsync());
            }

            Log.JsonInputSuccess(_logger, context.ModelType);

            return(await InputFormatterResult.SuccessAsync(model));
        }