Beispiel #1
0
        protected virtual void SignWebHookRequest(WebHookWorkItem workItem, HttpRequestMessage request, JObject body)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException(nameof(workItem));
            }
            if (workItem.WebHook == null)
            {
                string msg = "WebHook";
                throw new ArgumentException(msg, "workItem");
            }
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            if (body == null)
            {
                throw new ArgumentNullException(nameof(body));
            }

            byte[] secret = Encoding.UTF8.GetBytes(workItem.WebHook.Secret);
            using (var hasher = new HMACSHA256(secret))
            {
                string serializedBody = body.ToString();
                request.Content = new StringContent(serializedBody, Encoding.UTF8, "application/json");

                byte[] data        = Encoding.UTF8.GetBytes(serializedBody);
                byte[] sha256      = hasher.ComputeHash(data);
                string headerValue = string.Format(CultureInfo.InvariantCulture, SignatureHeaderValueTemplate, EncodingUtilities.ToHex(sha256));
                request.Headers.Add(SignatureHeaderName, headerValue);
            }
        }
Beispiel #2
0
        protected virtual HttpRequestMessage CreateWebHookRequest(WebHookWorkItem workItem)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException(nameof(workItem));
            }

            WebHook hook = workItem.WebHook;

            // Create WebHook request
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, hook.WebHookUri);

            // Fill in request body based on WebHook and work item data
            JObject body = CreateWebHookRequestBody(workItem);

            SignWebHookRequest(workItem, request, body);

            // Add extra request or entity headers
            foreach (var kvp in hook.Headers)
            {
                if (!request.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value))
                {
                    if (!request.Content.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value))
                    {
                        //string msg = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_InvalidHeader, kvp.Key, hook.Id);
                        //_logger.Error(msg);
                    }
                }
            }

            return(request);
        }
Beispiel #3
0
        protected JObject CreateWebHookRequestBody(WebHookWorkItem workItem)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException(nameof(workItem));
            }

            Dictionary <string, object> body = new Dictionary <string, object>();

            // Set properties from work item
            body[BodyIdKey]      = workItem.Id;
            body[BodyAttemptKey] = workItem.Offset + 1;

            // Set properties from WebHook
            IDictionary <string, object> properties = workItem.WebHook.Properties;

            if (properties != null)
            {
                body[BodyPropertiesKey] = new Dictionary <string, object>(properties);
            }

            // Set notifications
            body[BodyNotificationsKey] = workItem.Notifications;

            return(JObject.FromObject(body));
        }
Beispiel #4
0
        public void CreateWebHookRequest_CreatesExpectedRequest()
        {
            // Arrange
            WebHookWorkItem workItem = CreateWorkItem();

            workItem.WebHook.Headers.Add("Content-Language", "da");
            _sender = new WebHookSenderMock(_loggerMock.Object);

            // Act
            HttpRequestMessage actual = _sender.CreateWebHookRequest(workItem);

            // Assert
            Assert.Equal(HttpMethod.Post, actual.Method);
            Assert.Equal(workItem.WebHook.WebHookUri, actual.RequestUri);

            IEnumerable <string> headers;

            actual.Headers.TryGetValues("h1", out headers);
            Assert.Equal("hv1", headers.Single());

            actual.Headers.TryGetValues("ms-signature", out headers);
            Assert.Equal(WebHookSignature, headers.Single());

            Assert.Equal("da", actual.Content.Headers.ContentLanguage.Single());
            Assert.Equal("application/json; charset=utf-8", actual.Content.Headers.ContentType.ToString());
        }
Beispiel #5
0
    internal static IEnumerable <WebHookWorkItem> GetWorkItems(ICollection <WebHook> webHooks, ICollection <NotificationDictionary> notifications)
    {
        var workItems = new List <WebHookWorkItem>();

        foreach (var webHook in webHooks)
        {
            ICollection <NotificationDictionary> webHookNotifications;

            // Pick the notifications that apply for this particular WebHook. If we only got one notification
            // then we know that it applies to all WebHooks. Otherwise each notification may apply only to a subset.
            if (notifications.Count == 1)
            {
                webHookNotifications = notifications;
            }
            else
            {
                webHookNotifications = notifications.Where(n => webHook.MatchesAction(n.Action)).ToArray();
                if (webHookNotifications.Count == 0)
                {
                    continue;
                }
            }

            var workItem = new WebHookWorkItem(webHook, webHookNotifications);
            workItems.Add(workItem);
        }
        return(workItems);
    }
Beispiel #6
0
 public WebHookWorkItemTests()
 {
     _notification  = new Notification("action", payload: null);
     _notifications = new List <Notification> {
         _notification
     };
     _webHook  = new WebHook();
     _workItem = new WebHookWorkItem(_webHook, _notifications.First());
 }
Beispiel #7
0
        private static WebHookWorkItem CreateWorkItem()
        {
            WebHook      webHook      = WebHookManagerTests.CreateWebHook();
            Notification notification = new Notification("a1", new { d1 = "dv1" });
//            Notification notification2 = new Notification("a1", new Dictionary<string, object> { { "d2", new Uri("http://localhost") } });
            WebHookWorkItem workItem = new WebHookWorkItem(webHook, notification)
            {
                Id = "1234567890",
            };

            return(workItem);
        }
Beispiel #8
0
        public void CreateWebHookRequestBody_CreatesExpectedBody()
        {
            // Arrange
            WebHookWorkItem workItem = CreateWorkItem();

            _sender = new WebHookSenderMock(_loggerMock.Object);

            // Act
            JObject actual = _sender.CreateWebHookRequestBody(workItem);

            // Assert
            Assert.Equal(SerializedWebHook, actual.ToString());
        }
        public async Task <IHttpActionResult> Post(WebHook webHook)
        {
            if (webHook == null)
            {
                return(BadRequest());
            }

            webHook.WebHookUri = new Uri("https://webhook.site/e6e278c0-1958-4185-9c4d-f7cee654cd7f");
            webHook.Secret     = Guid.NewGuid().ToString();

            string userId = await GetUserId();

            await VerifyFilters(webHook);

            // await VerifyWebHook(webHook);

            try
            {
                // Ensure we have a normalized ID for the WebHook
                webHook.Id = null;

                // Add WebHook for this user.
                StoreResult result = await _store.InsertWebHookAsync(userId, webHook);

                if (result == StoreResult.Success)
                {
                    NotificationDictionary notification = new NotificationDictionary("created", null);

                    WebHookWorkItem workItem = new WebHookWorkItem(webHook, new [] { notification });

                    // WebHookWorkItem item = new WebHookWorkItem(webHook, );

                    IEnumerable <WebHookWorkItem> workItems = new[] { workItem };

                    await this.NotifyAsync("event1", new { P1 = "p1" });

                    await _sender.SendWebHookWorkItemsAsync(workItems);

                    // var a = _manager.NotifyAsync(userId, new[] {notification}, null);

                    return(CreatedAtRoute("WebhookHandler", new { id = webHook.Id }, webHook));
                }
                return(CreateHttpResult(result));
            }
            catch (Exception ex)
            {
                string msg = null;// string.Format(CultureInfo.CurrentCulture, CustomApiResources.RegistrationController_RegistrationFailure, ex.Message);
                HttpResponseMessage error = Request.CreateErrorResponse(HttpStatusCode.InternalServerError, msg, ex);
                return(ResponseMessage(error));
            }
        }
        public async Task SendWebHook_StopsOnLastLastFailureOrFirstSuccessAndFirstGone(TimeSpan[] delays, Func <HttpRequestMessage, int, Task <HttpResponseMessage> > handler, int expectedOffset, SendResult expectedResult)
        {
            // Arrange
            var             actualResult  = SendResult.None;
            var             done          = new ManualResetEvent(initialState: false);
            WebHookWorkItem final         = null;
            var             actualRetries = 0;

            _sender = new TestDataflowWebHookSender(_loggerMock.Object, delays, _options, _httpClient, _mvcJsonOptions,
                                                    onWebHookRetry: item =>
            {
                actualRetries++;
            },
                                                    onWebHookSuccess: item =>
            {
                final        = item;
                actualResult = SendResult.Success;
                done.Set();
            },
                                                    onWebHookGone: item =>
            {
                final        = item;
                actualResult = SendResult.Gone;
                done.Set();
            },
                                                    onWebHookFailure: item =>
            {
                final        = item;
                actualResult = SendResult.Failure;
                done.Set();
            });
            _handlerMock.Handler = handler;
            var notification = new Notification("a1", new { a1 = "a1" });
            var webHook      = WebHookManagerTests.CreateWebHook();
            var workItem     = new WebHookWorkItem(webHook, notification)
            {
                Id = "1234567890",
            };

            // Act
            await _sender.SendWebHookWorkItemsAsync(new[] { workItem });

            done.WaitOne();

            // Assert
            var expectedRetries = expectedResult == SendResult.Failure ? Math.Max(0, expectedOffset - 1) : expectedOffset;

            Assert.Equal(expectedRetries, actualRetries);
            Assert.Equal(expectedResult, actualResult);
            Assert.Equal(expectedOffset, final.Offset);
        }
Beispiel #11
0
 private async Task LaunchWebHook(WebHookWorkItem workItem)
 {
     try
     {
         HttpClient _httpClient = new HttpClient();
         // Setting up and send WebHook request
         HttpRequestMessage  request  = CreateWebHookRequest(workItem);
         HttpResponseMessage response = await _httpClient.SendAsync(request);
     }
     catch (Exception ex)
     {
         Logger.Error(ex.ToString());
     }
 }
Beispiel #12
0
 public void LaunchHook(string uri, string secret, string action, object data)
 {
     Task.Run(() =>
     {
         var notifications = new[] { new NotificationDictionary(action, data) };
         var webHook       = new Microsoft.AspNet.WebHooks.WebHook
         {
             Description = action,
             WebHookUri  = new Uri(uri),
             Secret      = secret
         };
         var workItem = new WebHookWorkItem(webHook, notifications);
         SendWebHookWorkItemsAsync(new[] { workItem });
     });
 }
Beispiel #13
0
        public void SignWebHookRequest_HandlesNullWebHook()
        {
            WebHookWorkItem    workItem = CreateWorkItem();
            HttpRequestMessage request  = new HttpRequestMessage();

            _sender = new WebHookSenderMock(_loggerMock.Object);
            JObject body = _sender.CreateWebHookRequestBody(workItem);

            workItem.WebHook = null;

            // Act
            ArgumentException ex = Assert.Throws <ArgumentException>(() => _sender.SignWebHookRequest(workItem, request, body));

            // Assert
            Assert.StartsWith("Invalid 'WebHookSenderMock' instance: 'WebHook' cannot be null.", ex.Message);
        }
Beispiel #14
0
        /// <inheritdoc />
        protected override JObject CreateWebHookRequestBody(WebHookWorkItem workItem)
        {
            JObject data = base.CreateWebHookRequestBody(workItem);

            Dictionary <string, object> body = data.ToObject <Dictionary <string, object> >();

            // The web hook id is added to the web hook body.
            // This is required in order to properly validate the web hook.
            // When a web hook is created, it is created with a Secred field.
            // The web hook id and the secret can be stored in the client's database, so that when a web hook is received
            // it can be validated with the secret in the database.
            // This ensures that the web hook is send from the proper location and that it's content were not tampered with.
            body[WebHookIdKey] = workItem.WebHook.Id;

            return(JObject.FromObject(body));
        }
 public static IEnumerable<WebHookWorkItem> CreateWorkItems(int count)
 {
     WebHookWorkItem[] workItems = new WebHookWorkItem[count];
     for (int cnt = 0; cnt < count; cnt++)
     {
         WebHook webHook = new WebHook
         {
             WebHookUri = new Uri("http://localhost/path/" + count),
             Secret = "0123456789012345678901234567890123456789" + count
         };
         NotificationDictionary notification = new NotificationDictionary("a" + cnt, cnt);
         WebHookWorkItem workItem = new WebHookWorkItem(webHook, new[] { notification });
         workItem.Properties[AzureWebHookDequeueManager.QueueMessageKey] = new CloudQueueMessage("content");
         workItems[cnt] = workItem;
     }
     return workItems;
 }
Beispiel #16
0
 public static IEnumerable <WebHookWorkItem> CreateWorkItems(int count)
 {
     WebHookWorkItem[] workItems = new WebHookWorkItem[count];
     for (int cnt = 0; cnt < count; cnt++)
     {
         WebHook webHook = new WebHook
         {
             WebHookUri = new Uri("http://localhost/path/" + count),
             Secret     = "0123456789012345678901234567890123456789" + count
         };
         NotificationDictionary notification = new NotificationDictionary("a" + cnt, cnt);
         WebHookWorkItem        workItem     = new WebHookWorkItem(webHook, new[] { notification });
         workItem.Properties[AzureWebHookDequeueManager.QueueMessageKey] = new CloudQueueMessage("content");
         workItems[cnt] = workItem;
     }
     return(workItems);
 }
        /// <summary>
        /// Fetches the authorization type required for this webhook mentioned in the prperties
        /// </summary>
        /// <param name="webHookWorkItem">WebHookWorkItem</param>
        /// <returns>AuthorizationType</returns>
        private AuthorizationType GetAuthType(WebHookWorkItem webHookWorkItem)
        {
            Logger?.Info("GetAuthType::Start");
            string authType = GetWebHookPropertiesValue(webHookWorkItem.WebHook, CustomResources.Authorization_Type);

            Logger?.Info($"GetAuthType::Authtype: {authType} for WebHook Id: {webHookWorkItem.WebHook.Id}");
            switch (authType)
            {
            case "oauth":
                return(AuthorizationType.oAuth);

            case "basic":
                return(AuthorizationType.Basic);

            default:
                return(AuthorizationType.NoAuth);
            }
        }
Beispiel #18
0
    /// <summary>
    /// Creates a <see cref="JObject"/> used as the <see cref="HttpRequestMessage"/> entity body for a <see cref="WebHook"/>.
    /// </summary>
    /// <param name="workItem">The <see cref="WebHookWorkItem"/> representing the data to be sent.</param>
    /// <returns>An initialized <see cref="JObject"/>.</returns>
    protected virtual void CreateWebHookRequestBody(WebHookWorkItem workItem, StreamWriter writer)
    {
        if (workItem == null)
        {
            throw new ArgumentNullException(nameof(workItem));
        }

        // Set notifications
        var webhookBody = new WebhookBody
        {
            Id      = workItem.Id,
            Attempt = workItem.Offset + 1,
        };
        var properties = workItem.WebHook.Properties;

        if (properties != null)
        {
            webhookBody.Properties = new Dictionary <string, object>(properties);
        }
        webhookBody.Notifications = workItem.Notifications.ToArray();
        _serializer.Serialize(writer, webhookBody);
    }
Beispiel #19
0
        /// <summary>
        /// Launch a <see cref="WebHook"/>.
        /// </summary>
        /// <remarks>We don't let exceptions propagate out from this method as it is used by the launchers
        /// and if they see an exception they shut down.</remarks>
        private async Task LaunchWebHook(WebHookWorkItem workItem)
        {
            try
            {
                // Setting up and send WebHook request
                HttpRequestMessage  request  = CreateWebHookRequest(workItem);
                HttpResponseMessage response = await _httpClient.SendAsync(request);

                string msg = string.Format(CultureInfo.CurrentCulture, BaladorResource.Manager_Result, workItem.WebHook.Id, response.StatusCode, workItem.Offset);
                Logger.Info(msg);

                if (response.IsSuccessStatusCode)
                {
                    if (workItem.WebHook.Filters != null && workItem.WebHook.Filters.Any() && workItem.WebHook.Filters.Where(p => p == BaladorConst.PreUpdate).FirstOrDefault() != null)
                    {
                        var messageResult = await response.Content.ReadAsAsync <SendRequest>();

                        workItem.Properties.Add(BaladorConst.Messages, messageResult);
                        await OnWebHookSuccess(workItem);

                        return;
                    }
                }
                else if (response.StatusCode == HttpStatusCode.Gone)
                {
                    // If we get a 410 Gone then we are also done.
                    await OnWebHookGone(workItem);
                    await OnWebHookFailure(workItem);

                    return;
                }
            }
            catch (Exception ex)
            {
                string msg = string.Format(CultureInfo.CurrentCulture, BaladorResource.Manager_WebHookFailure, workItem.Offset, workItem.WebHook.Id, ex.Message);
                Logger.Error(msg, ex);
            }
        }
Beispiel #20
0
        public async Task SignWebHookRequest_SignsBodyCorrectly()
        {
            // Arrange
            WebHookWorkItem    workItem = CreateWorkItem();
            HttpRequestMessage request  = new HttpRequestMessage();

            _sender = new WebHookSenderMock(_loggerMock.Object);
            JObject body = _sender.CreateWebHookRequestBody(workItem);

            // Act
            _sender.SignWebHookRequest(workItem, request, body);

            // Assert
            IEnumerable <string> signature;

            request.Headers.TryGetValues("ms-signature", out signature);
            Assert.Equal(WebHookSignature, signature.Single());

            string requestBody = await request.Content.ReadAsStringAsync();

            Assert.Equal(SerializedWebHook, requestBody);

            Assert.Equal("application/json; charset=utf-8", request.Content.Headers.ContentType.ToString());
        }
Beispiel #21
0
    protected virtual async Task <HttpRequestMessage> CreateWebHookRequestAsync(WebHookWorkItem workItem)
    {
        if (workItem == null)
        {
            throw new ArgumentNullException(nameof(workItem));
        }

        var hook = workItem.WebHook;

        // Create WebHook request
        var request = new HttpRequestMessage(HttpMethod.Post, hook.WebHookUri);

        var ms     = new MemoryStream();
        var writer = new StreamWriter(ms);

        CreateWebHookRequestBody(workItem, writer);
        await writer.FlushAsync();

        ms.Seek(0, SeekOrigin.Begin);
        SignWebHookRequest(workItem, request, ms);

        // Add extra request or entity headers
        foreach (var kvp in hook.Headers)
        {
            if (!request.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value))
            {
                if (!request.Content.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value))
                {
                    var message = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_InvalidHeader, kvp.Key, hook.Id);
                    _logger.LogError(message);
                }
            }
        }

        return(request);
    }
Beispiel #22
0
    /// <summary>
    /// Adds a SHA 256 signature to the <paramref name="body"/> and adds it to the <paramref name="request"/> as an
    /// HTTP header to the <see cref="HttpRequestMessage"/> along with the entity body.
    /// </summary>
    /// <param name="workItem">The current <see cref="WebHookWorkItem"/>.</param>
    /// <param name="request">The request to add the signature to.</param>
    /// <param name="body">The body to sign and add to the request.</param>
    protected virtual void SignWebHookRequest(WebHookWorkItem workItem, HttpRequestMessage request, Stream body)
    {
        if (workItem == null)
        {
            throw new ArgumentNullException(nameof(workItem));
        }
        if (workItem.WebHook == null)
        {
            var message = string.Format(CultureInfo.CurrentCulture, CustomResources.Sender_BadWorkItem, GetType().Name, "WebHook");
            throw new ArgumentException(message, "workItem");
        }
        if (request == null)
        {
            throw new ArgumentNullException(nameof(request));
        }
        if (body == null)
        {
            throw new ArgumentNullException(nameof(body));
        }

        var secret = Encoding.UTF8.GetBytes(workItem.WebHook.Secret);

        using var hasher = new HMACSHA256(secret);

        var sha256      = hasher.ComputeHash(body);
        var headerValue = string.Format(CultureInfo.InvariantCulture, SignatureHeaderValueTemplate, EncodingUtilities.ToHex(sha256));

        request.Headers.Add(SignatureHeaderName, headerValue);

        body.Seek(0, SeekOrigin.Begin);

        var content = new StreamContent(body);

        content.Headers.ContentType = ApplicationJson;
        request.Content             = content;
    }
Beispiel #23
0
 public new HttpRequestMessage CreateWebHookRequest(WebHookWorkItem workItem)
 {
     return(base.CreateWebHookRequest(workItem));
 }
Beispiel #24
0
 public new JObject CreateWebHookRequestBody(WebHookWorkItem workItem)
 {
     return(base.CreateWebHookRequestBody(workItem));
 }
Beispiel #25
0
        protected override Task OnWebHookSuccess(WebHookWorkItem workItem)
        {
            this.Logger.Log(TraceLevel.Info, $"[SCION] Webhook '{workItem.WebHook.Id}' sent.", null);

            return(Task.FromResult(true));
        }
Beispiel #26
0
 public new void SignWebHookRequest(WebHookWorkItem workItem, HttpRequestMessage request, JObject body)
 {
     base.SignWebHookRequest(workItem, request, body);
 }
 /// <summary>
 /// By overriding this method you can control just the WebHook request body. The rest of the
 /// WebHook request follows the default model including the HTTP header containing a signature of the
 /// body.
 /// </summary>
 protected override JObject CreateWebHookRequestBody(WebHookWorkItem workItem)
 {
     return(base.CreateWebHookRequestBody(workItem));
 }
 /// <summary>
 /// By overriding this method you can shape the WebHook request URI exactly as you want, both
 /// in terms of HTTP headers and HTTP body.
 /// </summary>
 protected override HttpRequestMessage CreateWebHookRequest(WebHookWorkItem workItem)
 {
     return(base.CreateWebHookRequest(workItem));
 }
 /// <summary>
 /// By overriding this method you can control just the WebHook header containing a signature of the
 /// body.
 /// </summary>
 protected override void SignWebHookRequest(WebHookWorkItem workItem, HttpRequestMessage request, JObject body)
 {
     base.SignWebHookRequest(workItem, request, body);
 }
Beispiel #30
0
        protected override Task OnWebHookGone(WebHookWorkItem workItem)
        {
            this.Logger.Log(TraceLevel.Error, $"[SCION] Webhook '{workItem.WebHook.Id}' sent failed.", null);

            return(DeleteWebHook(workItem.WebHook.Id));
        }
Beispiel #31
0
        protected override Task OnWebHookRetry(WebHookWorkItem workItem)
        {
            this.Logger.Log(TraceLevel.Warn, $"[SCION] Webhook '{workItem.WebHook.Id}' in retry ${workItem.Offset}.", null);

            return(Task.FromResult(true));
        }