public WebHookWorkItemTests() { _notification = new NotificationDictionary("action", data: null); _notifications = new List<NotificationDictionary> { _notification }; _webHook = new WebHook(); _workItem = new WebHookWorkItem(_webHook, _notifications); }
public void WebHookUri_Validates(Uri uri, ValidationOutcome expected) { // Arrange WebHook webHook = new WebHook { WebHookUri = uri }; var validationResults = new List<ValidationResult>(); var context = new ValidationContext(webHook) { MemberName = "WebHookUri" }; // Act bool actual = Validator.TryValidateProperty(webHook.WebHookUri, context, validationResults); // Assert switch (expected) { case ValidationOutcome.Valid: Assert.True(actual); break; case ValidationOutcome.Required: Assert.False(actual); Assert.Equal("The WebHookUri field is required.", validationResults.Single().ErrorMessage); Assert.Equal("WebHookUri", validationResults.Single().MemberNames.Single()); break; default: Assert.True(false); break; } }
private static void SubscribeNewUser() { var webhook = new WebHook(); webhook.Filters.Add("event1"); webhook.Properties.Add("StaticParamA", 10); webhook.Properties.Add("StaticParamB", 20); webhook.Secret = "PSBuMnbzZqVir4OnN4DE10IqB7HXfQ9l2"; webhook.WebHookUri = "http://www.alexmang.com"; _whStore.InsertWebHookAsync("user1", webhook); }
/// <inheritdoc /> public virtual async Task VerifyWebHookAsync(WebHook webHook) { if (webHook == null) { throw new ArgumentNullException(nameof(webHook)); } VerifySecret(webHook.Secret); VerifyUri(webHook.WebHookUri); await VerifyEchoAsync(webHook); }
/// <summary> /// Initializes a new instance of the <see cref="WebHookWorkItem"/> with the given <paramref name="notifications"/>. /// </summary> public WebHookWorkItem(WebHook webHook, IEnumerable<NotificationDictionary> notifications) { if (webHook == null) { throw new ArgumentNullException(nameof(webHook)); } if (notifications == null) { throw new ArgumentNullException(nameof(notifications)); } WebHook = webHook; _notifications = notifications; }
public async Task SendWebHookWorkItems_ThrowsException_IfInvalidWorkItem() { // Arrange WebHook webHook = new WebHook(); NotificationDictionary notification = new NotificationDictionary(); notification.Add("k", new SerializationFailure()); WebHookWorkItem workItem = new WebHookWorkItem(webHook, new[] { notification }); IEnumerable<WebHookWorkItem> workItems = new[] { workItem }; // Act InvalidOperationException ex = await Assert.ThrowsAsync<InvalidOperationException>(() => _sender.SendWebHookWorkItemsAsync(workItems)); // Assert Assert.Equal("Could not serialize message: Error getting value from 'Fail' on 'Microsoft.AspNet.WebHooks.AzureWebHookSenderTests+SerializationFailure'.", ex.Message); }
/// <inheritdoc/> public Task ValidateIdAsync(HttpRequestMessage request, WebHook webHook) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (webHook == null) { throw new ArgumentNullException(nameof(webHook)); } // Ensure we have a normalized ID for the WebHook webHook.Id = null; return Task.FromResult(true); }
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 }); }); }
private static WebHook SubscribeNewUser(string host) { var webhook = new WebHook(); //Alloy site will require this key in web.config to make the link string secretKey = "12345678901234567890123456789012"; string webHookUrl = "/api/webhooks/incoming/custom"; //action name, Client will register to. webhook.Filters.Add("entryupdating"); //A test paramameter webhook.Properties.Add("StaticParamA", 10); webhook.Secret = secretKey; webhook.WebHookUri = host + webHookUrl; //refister alloy site as user 1 in memory _whStore.InsertWebHookAsync("user1", webhook); return webhook; }
/// <inheritdoc /> public async Task VerifyWebHookAsync(WebHook webHook) { if (webHook == null) { throw new ArgumentNullException("webHook"); } // Check that we have a valid secret if (string.IsNullOrEmpty(webHook.Secret) || webHook.Secret.Length < 32 || webHook.Secret.Length > 64) { throw new InvalidOperationException(CustomResources.WebHook_InvalidSecret); } // Check that WebHook URI is either 'http' or 'https' if (!(webHook.WebHookUri.IsHttp() || webHook.WebHookUri.IsHttps())) { string msg = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_NoHttpUri, webHook.WebHookUri); _logger.Error(msg); throw new InvalidOperationException(msg); } // Create the echo query parameter that we want returned in response body as plain text. string echo = Guid.NewGuid().ToString("N"); HttpResponseMessage response; try { // Get request URI with echo query parameter UriBuilder webHookUri = new UriBuilder(webHook.WebHookUri); webHookUri.Query = EchoParameter + "=" + echo; // Create request adding any additional request headers (not entity headers) from Web Hook HttpRequestMessage hookRequest = new HttpRequestMessage(HttpMethod.Get, webHookUri.Uri); foreach (var kvp in webHook.Headers) { hookRequest.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value); } response = await _httpClient.SendAsync(hookRequest); } catch (Exception ex) { string msg = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_VerifyFailure, ex.Message); _logger.Error(msg, ex); throw new InvalidOperationException(msg); } if (!response.IsSuccessStatusCode) { string msg = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_VerifyFailure, response.StatusCode); _logger.Info(msg); throw new InvalidOperationException(msg); } // Verify response body if (response.Content == null) { string msg = CustomResources.Manager_VerifyNoBody; _logger.Error(msg); throw new InvalidOperationException(msg); } string actualEcho = await response.Content.ReadAsStringAsync(); if (!string.Equals(actualEcho, echo, StringComparison.Ordinal)) { string msg = CustomResources.Manager_VerifyBadEcho; _logger.Error(msg); throw new InvalidOperationException(msg); } }
internal static void SignWebHookRequest(WebHook webHook, HttpRequestMessage request, JObject body) { byte[] secret = Encoding.UTF8.GetBytes(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); } }
private static bool DefaultPredicate(WebHook webHook, string user) { return true; }
private static bool DefaultPredicate(WebHook webHook, string user) { return(true); }
internal static WebHook CreateWebHook(params string[] filters) { WebHook hook = new WebHook { Id = "1234", Description = "你好世界", Secret = "123456789012345678901234567890123456789012345678", WebHookUri = new Uri("http://localhost/hook"), }; hook.Headers.Add("h1", "hv1"); hook.Properties.Add("p1", "pv1"); foreach (string filter in filters) { hook.Filters.Add(filter); } return hook; }
/// <inheritdoc /> public override Task<StoreResult> UpdateWebHookAsync(string user, WebHook webHook) { if (user == null) { throw new ArgumentNullException(nameof(user)); } if (webHook == null) { throw new ArgumentNullException(nameof(webHook)); } user = NormalizeKey(user); ConcurrentDictionary<string, WebHook> userHooks; StoreResult result = StoreResult.NotFound; if (_store.TryGetValue(user, out userHooks)) { string id = NormalizeKey(webHook.Id); WebHook current; if (userHooks.TryGetValue(id, out current)) { bool updated = userHooks.TryUpdate(id, webHook, current); result = updated ? StoreResult.Success : StoreResult.Conflict; } } return Task.FromResult(result); }
/// <inheritdoc /> public abstract Task <StoreResult> InsertWebHookAsync(string user, WebHook webHook);
private void UpdateRegistrationFromWebHook(string user, WebHook webHook, Registration registration) { registration.User = user; registration.Id = webHook.Id; string content = JsonConvert.SerializeObject(webHook, _serializerSettings); string protectedData = _protector.Protect(content); registration.ProtectedData = protectedData; }
public WebHookTests() { _webHook = new WebHook(); }
/// <inheritdoc /> public override async Task<StoreResult> UpdateWebHookAsync(string user, WebHook webHook) { if (user == null) { throw new ArgumentNullException("user"); } if (webHook == null) { throw new ArgumentNullException("webHook"); } user = NormalizeKey(user); try { using (var context = new WebHookStoreContext()) { var registration = await context.Registrations.Where(r => r.User == user && r.Id == webHook.Id).FirstOrDefaultAsync(); if (registration == null) { return StoreResult.NotFound; } UpdateRegistrationFromWebHook(user, webHook, registration); context.Entry(registration).State = EntityState.Modified; await context.SaveChangesAsync(); } } catch (OptimisticConcurrencyException ocex) { string msg = string.Format(CultureInfo.CurrentCulture, SqlStorageResources.SqlStore_ConcurrencyError, "Update", ocex.Message); _logger.Error(msg, ocex); return StoreResult.Conflict; } catch (SqlException sqlex) { string msg = string.Format(CultureInfo.CurrentCulture, SqlStorageResources.SqlStore_SqlOperationFailed, "Update", sqlex.Message); _logger.Error(msg, sqlex); return StoreResult.OperationError; } catch (Exception ex) { string msg = string.Format(CultureInfo.CurrentCulture, SqlStorageResources.SqlStore_OperationFailed, "Update", ex.Message); _logger.Error(msg, ex); return StoreResult.InternalError; } return StoreResult.Success; }
private Registration ConvertFromWebHook(string user, WebHook webHook) { string content = JsonConvert.SerializeObject(webHook, _serializerSettings); string protectedData = _protector.Protect(content); var registration = new Registration { User = user, Id = webHook.Id, ProtectedData = protectedData }; return registration; }
/// <inheritdoc /> public override async Task<StoreResult> InsertWebHookAsync(string user, WebHook webHook) { if (user == null) { throw new ArgumentNullException("user"); } if (webHook == null) { throw new ArgumentNullException("webHook"); } user = NormalizeKey(user); try { using (var context = new WebHookStoreContext()) { var registration = ConvertFromWebHook(user, webHook); context.Registrations.Attach(registration); context.Entry(registration).State = EntityState.Added; await context.SaveChangesAsync(); } } catch (DbUpdateException uex) { string error = uex.GetBaseException().Message; string msg = string.Format(CultureInfo.CurrentCulture, SqlStorageResources.SqlStore_SqlOperationFailed, "Insert", error); _logger.Error(msg, uex); return StoreResult.Conflict; } catch (OptimisticConcurrencyException ocex) { string msg = string.Format(CultureInfo.CurrentCulture, SqlStorageResources.SqlStore_ConcurrencyError, "Insert", ocex.Message); } catch (SqlException sqlex) { string msg = string.Format(CultureInfo.CurrentCulture, SqlStorageResources.SqlStore_SqlOperationFailed, "Insert", sqlex.Message); _logger.Error(msg, sqlex); return StoreResult.OperationError; } catch (Exception ex) { string msg = string.Format(CultureInfo.CurrentCulture, SqlStorageResources.SqlStore_OperationFailed, "Insert", ex.Message); _logger.Error(msg, ex); return StoreResult.InternalError; } return StoreResult.Success; }
/// <inheritdoc /> public abstract Task RegisterAsync(HttpRequestMessage request, WebHook webHook);
protected static WebHook CreateWebHook(string user, int offset, string filter = "a1") { WebHook hook = new WebHook { Id = offset.ToString(), Description = user, Secret = "123456789012345678901234567890123456789012345678", WebHookUri = "http://localhost/hook/" + offset }; hook.Headers.Add("h1", "hv1"); hook.Properties.Add("p1", "pv1"); hook.Filters.Add(filter); return hook; }
protected static WebHook CreateWebHook(string user, int offset, string filter = "a1", bool isPaused = false, bool hasWildcard = false) { WebHook hook = new WebHook { Id = offset.ToString(), IsPaused = isPaused, Description = user, Secret = "123456789012345678901234567890123456789012345678", WebHookUri = new Uri("http://localhost/hook/" + offset) }; hook.Headers.Add("h1", "hv1"); hook.Properties.Add("p1", "pv1"); hook.Filters.Add(filter); if (hasWildcard) { hook.Filters.Add(WildcardWebHookFilterProvider.Name); } return hook; }
/// <summary> /// Determines whether any of the given <paramref name="actions"/> match the filters for a given <see cref="WebHook"/>. /// The actions can either match a filter directly or match a wildcard. /// </summary> /// <param name="webHook">The <see cref="WebHook"/> instance to operate on.</param> /// <param name="actions">The set of actions to match against the <paramref name="webHook"/> filters.</param> /// <returns><c>true</c> if one or more of the <paramref name="actions"/> match, otherwise <c>false</c>.</returns> public static bool MatchesAnyAction(this WebHook webHook, IEnumerable <string> actions) { return(webHook != null && actions != null && (webHook.Filters.Contains(WildcardWebHookFilterProvider.Name) || actions.FirstOrDefault(f => webHook.Filters.Contains(f)) != null)); }
public WebHookTests() { _webHook = new WebHook(); }
/// <summary> /// Checks that the given <paramref name="webHook"/> is not paused and matches at least /// one of the given <paramref name="actions"/>. /// </summary> /// <param name="webHook">The <see cref="WebHook"/> instance to operate on.</param> /// <param name="actions">The set of actions to match against the <paramref name="webHook"/> filters.</param> /// <returns><c>true</c> if the given <paramref name="webHook"/> matches one of the pro</returns> protected virtual bool MatchesAnyAction(WebHook webHook, IEnumerable <string> actions) { return(webHook != null && !webHook.IsPaused && webHook.MatchesAnyAction(actions)); }
/// <inheritdoc /> public override Task<StoreResult> InsertWebHookAsync(string user, WebHook webHook) { if (user == null) { throw new ArgumentNullException(nameof(user)); } if (webHook == null) { throw new ArgumentNullException(nameof(webHook)); } user = NormalizeKey(user); ConcurrentDictionary<string, WebHook> userHooks = _store.GetOrAdd(user, key => new ConcurrentDictionary<string, WebHook>()); string id = NormalizeKey(webHook.Id); bool inserted = userHooks.TryAdd(id, webHook); StoreResult result = inserted ? StoreResult.Success : StoreResult.Conflict; return Task.FromResult(result); }
/// <summary> /// Determines whether a given <paramref name="action"/> matches the filters for a given <see cref="WebHook"/>. /// The action can either match a filter directly or match a wildcard. /// </summary> /// <param name="webHook">The <see cref="WebHook"/> instance to operate on.</param> /// <param name="action">The action to match against the <paramref name="webHook"/> filters.</param> /// <returns><c>true</c> if the <paramref name="action"/> matches, otherwise <c>false</c>.</returns> public static bool MatchesAction(this WebHook webHook, string action) { return(webHook != null && (webHook.Filters.Contains(WildcardWebHookFilterProvider.Name) || webHook.Filters.Contains(action))); }
/// <summary> /// Verifies the WebHook by submitting a GET request with a query token intended by the echoed back. /// </summary> /// <param name="webHook">The <see cref="WebHook"/> to verify.</param> protected virtual async Task VerifyEchoAsync(WebHook webHook) { // Create the echo query parameter that we want returned in response body as plain text. string echo = Guid.NewGuid().ToString("N"); HttpResponseMessage response; try { // If WebHook URI contains a "NoEcho" query parameter then we don't verify the URI using a GET request NameValueCollection parameters = webHook.WebHookUri.ParseQueryString(); if (parameters[NoEchoParameter] != null) { string msg = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_NoEcho); _logger.LogInformation(msg); return; } // Get request URI with echo query parameter UriBuilder webHookUri = new UriBuilder(webHook.WebHookUri); webHookUri.Query = EchoParameter + "=" + echo; // Create request adding any additional request headers (not entity headers) from Web Hook HttpRequestMessage hookRequest = new HttpRequestMessage(HttpMethod.Get, webHookUri.Uri); foreach (var kvp in webHook.Headers) { hookRequest.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value); } response = await _httpClient.SendAsync(hookRequest); } catch (Exception ex) { string msg = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_VerifyFailure, ex.Message); _logger.LogError(msg, ex); throw new InvalidOperationException(msg); } if (!response.IsSuccessStatusCode) { string msg = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_VerifyFailure, response.StatusCode); _logger.LogInformation(msg); throw new InvalidOperationException(msg); } // Verify response body if (response.Content == null) { string msg = CustomResources.Manager_VerifyNoBody; _logger.LogError(msg); throw new InvalidOperationException(msg); } string actualEcho = await response.Content.ReadAsStringAsync(); if (!string.Equals(actualEcho, echo, StringComparison.Ordinal)) { string msg = CustomResources.Manager_VerifyBadEcho; _logger.LogError(msg); throw new InvalidOperationException(msg); } }
/// <inheritdoc /> public abstract Task <StoreResult> UpdateWebHookAsync(string user, WebHook webHook);
private DynamicTableEntity ConvertFromWebHook(string partitionKey, string rowKey, WebHook webHook) { DynamicTableEntity entity = new DynamicTableEntity(partitionKey, rowKey); entity.ETag = "*"; // Set data column with encrypted serialization of WebHook string content = JsonConvert.SerializeObject(webHook, _serializerSettings); string encryptedContent = _protector.Protect(content); EntityProperty property = EntityProperty.GeneratePropertyForString(encryptedContent); entity.Properties.Add(WebHookDataColumn, property); return(entity); }
/// <inheritdoc /> public abstract Task<StoreResult> InsertWebHookAsync(string user, WebHook webHook);
/// <inheritdoc /> public async Task VerifyWebHookAsync(WebHook webHook) { if (webHook == null) { throw new ArgumentNullException("webHook"); } // Create the echo query parameter that we want returned in response body as plain text. string echo = Guid.NewGuid().ToString("N"); HttpResponseMessage response; try { // Get request URI with echo query parameter UriBuilder webHookUri = new UriBuilder(webHook.WebHookUri); webHookUri.Query = EchoParameter + "=" + echo; // Create request adding any additional request headers (not entity headers) from Web Hook HttpRequestMessage hookRequest = new HttpRequestMessage(HttpMethod.Get, webHookUri.Uri); foreach (var kvp in webHook.Headers) { hookRequest.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value); } response = await _httpClient.SendAsync(hookRequest); } catch (Exception ex) { string msg = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_VerifyFailure, ex.Message); _logger.Error(msg, ex); throw new InvalidOperationException(msg); } if (!response.IsSuccessStatusCode) { string msg = string.Format(CultureInfo.CurrentCulture, CustomResources.Manager_VerifyFailure, response.StatusCode); _logger.Info(msg); throw new InvalidOperationException(msg); } // Verify response body if (response.Content == null) { string msg = CustomResources.Manager_VerifyNoBody; _logger.Error(msg); throw new InvalidOperationException(msg); } string actualEcho = await response.Content.ReadAsStringAsync(); if (!string.Equals(actualEcho, echo, StringComparison.Ordinal)) { string msg = CustomResources.Manager_VerifyBadEcho; _logger.Error(msg); throw new InvalidOperationException(msg); } }
/// <inheritdoc /> public abstract Task<StoreResult> UpdateWebHookAsync(string user, WebHook webHook);
/// <inheritdoc /> public override async Task<StoreResult> UpdateWebHookAsync(string user, WebHook webHook) { if (user == null) { throw new ArgumentNullException("user"); } if (webHook == null) { throw new ArgumentNullException("webHook"); } user = NormalizeKey(user); string id = NormalizeKey(webHook.Id); CloudTable table = _manager.GetCloudTable(_connectionString, WebHookTable); DynamicTableEntity tableEntity = ConvertFromWebHook(user, id, webHook); TableOperation operation = TableOperation.Replace(tableEntity); TableResult tableResult = await _manager.ExecuteAsync(table, operation); StoreResult result = GetStoreResult(tableResult); if (result != StoreResult.Success) { string msg = string.Format(CultureInfo.CurrentCulture, AzureStorageResources.StorageManager_OperationFailed, table.Name, tableResult.HttpStatusCode); _logger.Error(msg); } return result; }
/// <summary> /// Checks that the given <paramref name="webHook"/> is not paused and matches at least /// one of the given <paramref name="actions"/>. /// </summary> /// <param name="webHook">The <see cref="WebHook"/> instance to operate on.</param> /// <param name="actions">The set of actions to match against the <paramref name="webHook"/> filters.</param> /// <returns><c>true</c> if the given <paramref name="webHook"/> matches one of the pro</returns> protected virtual bool MatchesAnyAction(WebHook webHook, IEnumerable<string> actions) { return webHook != null && !webHook.IsPaused && webHook.MatchesAnyAction(actions); }
private DynamicTableEntity ConvertFromWebHook(string partitionKey, string rowKey, WebHook webHook) { DynamicTableEntity entity = new DynamicTableEntity(partitionKey, rowKey); entity.ETag = "*"; // Set data column with encrypted serialization of WebHook string content = JsonConvert.SerializeObject(webHook, _serializerSettings); string encryptedContent = _protector.Protect(content); EntityProperty property = EntityProperty.GeneratePropertyForString(encryptedContent); entity.Properties.Add(WebHookDataColumn, property); return entity; }
public void Serializes_AsExpected() { // Arrange WebHook webHook = new WebHook { Description = "你好", Id = "1234567890", IsPaused = true, Secret = "世界", WebHookUri = new Uri("http://localhost/path"), }; webHook.Filters.Add("*"); webHook.Headers.Add(new KeyValuePair<string, string>("k1", "v1")); webHook.Properties.Add(new KeyValuePair<string, object>("p1", 1234)); // Act/Assert SerializationAssert.SerializesAs<WebHook>(webHook, _settings, "{\"Id\":\"1234567890\",\"WebHookUri\":\"http://localhost/path\",\"Secret\":\"世界\",\"Description\":\"你好\",\"IsPaused\":true,\"Filters\":[\"*\"],\"Headers\":{\"k1\":\"v1\"},\"Properties\":{\"p1\":1234}}"); }