protected virtual async Task EnsureValidCode(HttpRequestMessage request, string id) { if (request == null) { throw new ArgumentNullException("request"); } EnsureSecureConnection(request); NameValueCollection queryParameters = request.RequestUri.ParseQueryString(); string code = queryParameters[CodeQueryParameter]; if (string.IsNullOrEmpty(code)) { string msg = string.Format(CultureInfo.CurrentCulture, ReceiverResources.Receiver_NoCode, CodeQueryParameter); request.GetConfiguration().DependencyResolver.GetLogger().Error(msg); HttpResponseMessage noCode = request.CreateErrorResponse(HttpStatusCode.BadRequest, msg); throw new HttpResponseException(noCode); } string secretKey = await this.GetReceiverConfig(request, Name, id, CodeMinLength, CodeMaxLength); if (!WebHookReceiver.SecretEqual(code, secretKey)) { string msg = string.Format(CultureInfo.CurrentCulture, ReceiverResources.Receiver_BadCode, CodeQueryParameter); request.GetConfiguration().DependencyResolver.GetLogger().Error(msg); HttpResponseMessage invalidCode = request.CreateErrorResponse(HttpStatusCode.BadRequest, msg); throw new HttpResponseException(invalidCode); } }
/// <summary> /// Verifies that the signature header matches that of the actual body. /// </summary> protected virtual async Task <bool> VerifySignature(string id, HttpRequestMessage request) { string secretKey = await GetReceiverConfig(request, Name, id, SecretMinLength, SecretMaxLength); // Get the expected hash from the signature header string signatureHeaderValue = GetRequestHeader(request, SignatureHeaderName); byte[] expectedHash; try { expectedHash = EncodingUtilities.FromHex(signatureHeaderValue); } catch (Exception ex) { string msg = string.Format(CultureInfo.CurrentCulture, InstagramReceiverResources.Receiver_BadHeaderEncoding, SignatureHeaderName); request.GetConfiguration().DependencyResolver.GetLogger().Error(msg, ex); HttpResponseMessage invalidEncoding = request.CreateErrorResponse(HttpStatusCode.BadRequest, msg); throw new HttpResponseException(invalidEncoding); } // Get the actual hash of the request body concatenated with the request URI byte[] actualHash; byte[] secret = Encoding.UTF8.GetBytes(secretKey); using (var hasher = new HMACSHA1(secret)) { byte[] data = await request.Content.ReadAsByteArrayAsync(); actualHash = hasher.ComputeHash(data); } // Now verify that the provided hash matches the expected hash. return(WebHookReceiver.SecretEqual(expectedHash, actualHash)); }
/// <inheritdoc /> public override async Task <HttpResponseMessage> ReceiveAsync(string id, HttpRequestContext context, HttpRequestMessage request) { if (id == null) { throw new ArgumentNullException("id"); } if (context == null) { throw new ArgumentNullException("context"); } if (request == null) { throw new ArgumentNullException("request"); } if (request.Method == HttpMethod.Post) { EnsureSecureConnection(request); // Read the request entity body NameValueCollection data = await ReadAsFormDataAsync(request); // Verify that the token is correct string token = data[TokenParameter]; string secretKey = await GetReceiverConfig(request, Name, id, SecretMinLength, SecretMaxLength); if (!WebHookReceiver.SecretEqual(token, secretKey)) { string msg = string.Format(CultureInfo.CurrentCulture, SlackReceiverResources.Receiver_BadToken, TokenParameter); context.Configuration.DependencyResolver.GetLogger().Error(msg); HttpResponseMessage invalidCode = request.CreateErrorResponse(HttpStatusCode.BadRequest, msg); return(invalidCode); } // Get the action string action = data[ActionParameter]; if (string.IsNullOrEmpty(action)) { string msg = string.Format(CultureInfo.CurrentCulture, SlackReceiverResources.Receiver_BadBody, ActionParameter); context.Configuration.DependencyResolver.GetLogger().Error(msg); HttpResponseMessage badType = request.CreateErrorResponse(HttpStatusCode.BadRequest, msg); return(badType); } // Get the subtext by removing the trigger word string text = data[TextParameter]; data[SubtextParameter] = GetSubtext(action, text); // Call registered handlers return(await ExecuteWebHookAsync(id, context, request, new string[] { action }, data)); } else { return(CreateBadMethodResponse(request)); } }
public void SecretEqual_ComparesStringsCorrectly(string inputA, string inputB, bool expected) { // Arrange Initialize(TestSecret); // Act bool actual = WebHookReceiver.SecretEqual(inputA, inputB); // Assert Assert.Equal(expected, actual); }
public void SecretEqual_ComparesByteArraysCorrectly(byte[] inputA, byte[] inputB, bool expected) { // Arrange Initialize(TestSecret); // Act var actual = WebHookReceiver.SecretEqual(inputA, inputB); // Assert Assert.Equal(expected, actual); }
/// <summary> /// Verifies that the signature header matches that of the actual body. /// </summary> protected virtual async Task VerifySignature(string id, HttpRequestMessage request) { var secretKey = await GetReceiverConfig(request, Name, id, SecretMinLength, SecretMaxLength); // Get the expected hash from the signature header var header = GetRequestHeader(request, SignatureHeaderName); var values = header.SplitAndTrim('='); if (values.Length != 2 || !string.Equals(values[0], SignatureHeaderKey, StringComparison.OrdinalIgnoreCase)) { var message = string.Format(CultureInfo.CurrentCulture, GitHubReceiverResources.Receiver_BadHeaderValue, SignatureHeaderName, SignatureHeaderKey, "<value>"); request.GetConfiguration().DependencyResolver.GetLogger().Error(message); var invalidHeader = request.CreateErrorResponse(HttpStatusCode.BadRequest, message); throw new HttpResponseException(invalidHeader); } byte[] expectedHash; try { expectedHash = EncodingUtilities.FromHex(values[1]); } catch (Exception ex) { var message = string.Format(CultureInfo.CurrentCulture, GitHubReceiverResources.Receiver_BadHeaderEncoding, SignatureHeaderName); request.GetConfiguration().DependencyResolver.GetLogger().Error(message, ex); var invalidEncoding = request.CreateErrorResponse(HttpStatusCode.BadRequest, message); throw new HttpResponseException(invalidEncoding); } // Get the actual hash of the request body byte[] actualHash; var secret = Encoding.UTF8.GetBytes(secretKey); using (var hasher = new HMACSHA1(secret)) { var data = await request.Content.ReadAsByteArrayAsync(); actualHash = hasher.ComputeHash(data); } // Now verify that the provided hash matches the expected hash. if (!WebHookReceiver.SecretEqual(expectedHash, actualHash)) { var badSignature = CreateBadSignatureResponse(request, SignatureHeaderName); throw new HttpResponseException(badSignature); } }
/// <summary> /// Verifies that the signature header matches that of the actual body. /// </summary> protected virtual async Task <bool> VerifySignature(string id, HttpRequestMessage request) { // Get the expected hash from the signature and app key headers string signatureHeaderValue = GetRequestHeader(request, SignatureHeaderName); string keyHeaderValue = GetRequestHeader(request, KeyHeaderName); // Lookup which secret to use based on key header value IDictionary <string, string> lookupTable = await GetSecretLookupTable(id, request); string secretKey; if (!lookupTable.TryGetValue(keyHeaderValue, out secretKey)) { string msg = string.Format(CultureInfo.CurrentCulture, PusherReceiverResources.Receiver_SecretNotFound, KeyHeaderName, keyHeaderValue); request.GetConfiguration().DependencyResolver.GetLogger().Error(msg); HttpResponseMessage invalidEncoding = request.CreateErrorResponse(HttpStatusCode.BadRequest, msg); throw new HttpResponseException(invalidEncoding); } byte[] expectedHash; try { expectedHash = EncodingUtilities.FromHex(signatureHeaderValue); } catch (Exception ex) { string msg = string.Format(CultureInfo.CurrentCulture, PusherReceiverResources.Receiver_BadHeaderEncoding, SignatureHeaderName); request.GetConfiguration().DependencyResolver.GetLogger().Error(msg, ex); HttpResponseMessage invalidEncoding = request.CreateErrorResponse(HttpStatusCode.BadRequest, msg); throw new HttpResponseException(invalidEncoding); } // Get the actual hash of the request body byte[] actualHash; byte[] secret = Encoding.UTF8.GetBytes(secretKey); using (var hasher = new HMACSHA256(secret)) { byte[] data = await request.Content.ReadAsByteArrayAsync(); actualHash = hasher.ComputeHash(data); } // Now verify that the provided hash matches the expected hash. return(WebHookReceiver.SecretEqual(expectedHash, actualHash)); }
/// <summary> /// Verifies that the signature header matches that of the actual body. /// </summary> protected virtual async Task <bool> VerifySignature(HttpRequestMessage request, string id) { var secretKey = await GetReceiverConfig(request, Name, id, SecretMinLength, SecretMaxLength); // Get the expected hash from the signature header var signatureHeaderValue = GetRequestHeader(request, SignatureHeaderName); byte[] expectedHash; try { expectedHash = EncodingUtilities.FromBase64(signatureHeaderValue); } catch (Exception ex) { var message = string.Format(CultureInfo.CurrentCulture, TrelloResources.Receiver_BadHeaderEncoding, SignatureHeaderName); request.GetConfiguration().DependencyResolver.GetLogger().Error(message, ex); var invalidEncoding = request.CreateErrorResponse(HttpStatusCode.BadRequest, message); throw new HttpResponseException(invalidEncoding); } // Get the actual hash of the request body concatenated with the request URI byte[] actualHash; var secret = Encoding.UTF8.GetBytes(secretKey); using (var hasher = new HMACSHA1(secret)) { var data = await request.Content.ReadAsByteArrayAsync(); var requestUri = Encoding.UTF8.GetBytes(request.RequestUri.AbsoluteUri); var combinedBytes = new byte[data.Length + requestUri.Length]; Buffer.BlockCopy(data, 0, combinedBytes, 0, data.Length); Buffer.BlockCopy(requestUri, 0, combinedBytes, data.Length, requestUri.Length); actualHash = hasher.ComputeHash(combinedBytes); } // Now verify that the provided hash matches the expected hash. return(WebHookReceiver.SecretEqual(expectedHash, actualHash)); }
/// <inheritdoc /> public override async Task <HttpResponseMessage> ReceiveAsync(string id, HttpRequestContext context, HttpRequestMessage request) { if (id == null) { throw new ArgumentNullException(nameof(id)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } if (request == null) { throw new ArgumentNullException(nameof(request)); } if (request.Method == HttpMethod.Post) { EnsureSecureConnection(request); // Read the request entity body var data = await ReadAsXmlAsync(request); var notifications = new SalesforceNotifications(data); // Ensure that the organization ID matches the expected value. var orgId = GetShortOrgId(notifications.OrganizationId); var secret = await GetReceiverConfig(request, ReceiverConfigName, id, 15, 18); var secretKey = GetShortOrgId(secret); if (!WebHookReceiver.SecretEqual(orgId, secretKey)) { var message = string.Format(CultureInfo.CurrentCulture, SalesforceReceiverResources.Receiver_BadValue, "OrganizationId"); context.Configuration.DependencyResolver.GetLogger().Error(message); var fault = string.Format(CultureInfo.InvariantCulture, ReadResource("Microsoft.AspNet.WebHooks.Messages.FaultResponse.xml"), message); var invalidId = GetXmlResponse(request, HttpStatusCode.BadRequest, fault); return(invalidId); } // Get the action var action = notifications.ActionId; if (string.IsNullOrEmpty(action)) { var message = string.Format(CultureInfo.CurrentCulture, SalesforceReceiverResources.Receiver_BadBody, "ActionId"); context.Configuration.DependencyResolver.GetLogger().Error(message); var fault = string.Format(CultureInfo.InvariantCulture, ReadResource("Microsoft.AspNet.WebHooks.Messages.FaultResponse.xml"), message); var badType = GetXmlResponse(request, HttpStatusCode.BadRequest, fault); return(badType); } // Call registered handlers var response = await ExecuteWebHookAsync(id, context, request, new string[] { action }, notifications); // Add SOAP response content if not already present or isn't XML. Ignore current (e.g. JSON) content. if (response?.Content == null || !response.Content.IsXml()) { // Ignore redirects because SOAP 1.1 doesn't mention them and they're corner cases in SOAP. var statusCode = response?.StatusCode ?? HttpStatusCode.OK; if (statusCode >= (HttpStatusCode)200 && statusCode < (HttpStatusCode)300) { var success = ReadResource("Microsoft.AspNet.WebHooks.Messages.NotificationResponse.xml"); response = GetXmlResponse(request, statusCode, success); } else { // Move failure information into a SOAP fault response. Fault contains code soapenv:Client and // that must be transmitted with HTTP status 400, Bad Request according to SOAP 1.2 (mixing // that sensible choice into this SOAP 1.1 implementation). var resource = ReadResource("Microsoft.AspNet.WebHooks.Messages.FaultResponse.xml"); var faultString = string.Format( CultureInfo.CurrentCulture, SalesforceReceiverResources.Receiver_HandlerFailed, statusCode, response.ReasonPhrase); var failure = string.Format(CultureInfo.InvariantCulture, resource, faultString); response = GetXmlResponse(request, HttpStatusCode.BadRequest, failure); } } return(response); } else { return(CreateBadMethodResponse(request)); } }
/// <inheritdoc /> public override async Task <HttpResponseMessage> ReceiveAsync(string id, HttpRequestContext context, HttpRequestMessage request) { if (id == null) { throw new ArgumentNullException(nameof(id)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } if (request == null) { throw new ArgumentNullException(nameof(request)); } if (request.Method == HttpMethod.Post) { EnsureSecureConnection(request); // Read the request entity body var data = await ReadAsFormDataAsync(request); // Verify that the token is correct var token = data[TokenParameter]; var secretKey = await GetReceiverConfig(request, Name, id, SecretMinLength, SecretMaxLength); if (!WebHookReceiver.SecretEqual(token, secretKey)) { var message = string.Format(CultureInfo.CurrentCulture, SlackReceiverResources.Receiver_BadToken, TokenParameter); context.Configuration.DependencyResolver.GetLogger().Error(message); var invalidCode = request.CreateErrorResponse(HttpStatusCode.BadRequest, message); return(invalidCode); } // Get the action by looking for either trigger_word or command parameter var action = string.Empty; if (data[TriggerParameter] != null) { // Trigger parameter was supplied // Get the subtext by removing the trigger word action = data[TriggerParameter]; var text = data[TextParameter]; data[SubtextParameter] = GetSubtext(action, text); } else if (data[CommandParameter] != null) { // Command parameter was supplied action = data[CommandParameter]; } else { // Trigger was omitted as optional // Set the subtext to the full text action = data[TextParameter]; data[SubtextParameter] = data[TextParameter]; } if (string.IsNullOrEmpty(action)) { var message = string.Format(CultureInfo.CurrentCulture, SlackReceiverResources.Receiver_BadBody, CommandParameter, TriggerParameter); context.Configuration.DependencyResolver.GetLogger().Error(message); var badType = request.CreateErrorResponse(HttpStatusCode.BadRequest, message); return(badType); } // Call registered handlers return(await ExecuteWebHookAsync(id, context, request, new string[] { action }, data)); } else { return(CreateBadMethodResponse(request)); } }
/// <inheritdoc /> public override async Task <HttpResponseMessage> ReceiveAsync(string id, HttpRequestContext context, HttpRequestMessage request) { if (id == null) { throw new ArgumentNullException(nameof(id)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } if (request == null) { throw new ArgumentNullException(nameof(request)); } if (request.Method == HttpMethod.Post) { EnsureSecureConnection(request); // Read the request entity body var data = await ReadAsXmlAsync(request); var notifications = new SalesforceNotifications(data); // Ensure that the organization ID matches the expected value. var orgId = GetShortOrgId(notifications.OrganizationId); var secret = await GetReceiverConfig(request, ReceiverConfigName, id, 15, 18); var secretKey = GetShortOrgId(secret); if (!WebHookReceiver.SecretEqual(orgId, secretKey)) { var message = string.Format(CultureInfo.CurrentCulture, SalesforceReceiverResources.Receiver_BadValue, "OrganizationId"); context.Configuration.DependencyResolver.GetLogger().Error(message); var fault = string.Format(CultureInfo.InvariantCulture, ReadResource("Microsoft.AspNet.WebHooks.Messages.FaultResponse.xml"), message); var invalidId = GetXmlResponse(request, HttpStatusCode.BadRequest, fault); return(invalidId); } // Get the action var action = notifications.ActionId; if (string.IsNullOrEmpty(action)) { var message = string.Format(CultureInfo.CurrentCulture, SalesforceReceiverResources.Receiver_BadBody, "ActionId"); context.Configuration.DependencyResolver.GetLogger().Error(message); var fault = string.Format(CultureInfo.InvariantCulture, ReadResource("Microsoft.AspNet.WebHooks.Messages.FaultResponse.xml"), message); var badType = GetXmlResponse(request, HttpStatusCode.BadRequest, fault); return(badType); } // Call registered handlers var response = await ExecuteWebHookAsync(id, context, request, new string[] { action }, notifications); // Add SOAP response if not already present if (response == null || response.Content == null || !response.Content.IsXml()) { var success = ReadResource("Microsoft.AspNet.WebHooks.Messages.NotificationResponse.xml"); response = GetXmlResponse(request, HttpStatusCode.OK, success); } return(response); } else { return(CreateBadMethodResponse(request)); } }
/// <inheritdoc /> public override async Task <HttpResponseMessage> ReceiveAsync(string id, HttpRequestContext context, HttpRequestMessage request) { if (id == null) { throw new ArgumentNullException("id"); } if (context == null) { throw new ArgumentNullException("context"); } if (request == null) { throw new ArgumentNullException("request"); } if (request.Method == HttpMethod.Post) { EnsureSecureConnection(request); // Read the request entity body XElement data = await ReadAsXmlAsync(request); SalesforceNotifications notifications = new SalesforceNotifications(data); // Ensure that the organization ID matches the expected value. string orgId = GetShortOrgId(notifications.OrganizationId); string secret = await GetReceiverConfig(request, ReceiverConfigName, id, 15, 18); string secretKey = GetShortOrgId(secret); if (!WebHookReceiver.SecretEqual(orgId, secretKey)) { string msg = string.Format(CultureInfo.CurrentCulture, SalesforceReceiverResources.Receiver_BadValue, "OrganizationId"); context.Configuration.DependencyResolver.GetLogger().Error(msg); HttpResponseMessage invalidId = request.CreateErrorResponse(HttpStatusCode.BadRequest, msg); return(invalidId); } // Get the action string action = notifications.ActionId; if (string.IsNullOrEmpty(action)) { string msg = string.Format(CultureInfo.CurrentCulture, SalesforceReceiverResources.Receiver_BadBody, "ActionId"); context.Configuration.DependencyResolver.GetLogger().Error(msg); HttpResponseMessage badType = request.CreateErrorResponse(HttpStatusCode.BadRequest, msg); return(badType); } // Call registered handlers HttpResponseMessage response = await ExecuteWebHookAsync(id, context, request, new string[] { action }, notifications); // Add SOAP response if not already present if (response == null || response.Content == null || !response.Content.IsXml()) { response = request.CreateResponse(); string ack = ReadResource("Microsoft.AspNet.WebHooks.Messages.NotificationResponse.xml"); response.Content = new StringContent(ack, Encoding.UTF8, "application/xml"); } return(response); } else { return(CreateBadMethodResponse(request)); } }