/// <summary> /// Filters validation /// </summary> /// <param name="webHook"></param> /// <param name="cancellationToken"></param> /// <returns></returns> /// <exception cref="ArgumentException">No filter, or incorrect filters</exception> protected virtual Task VerifyFiltersAsync(IWebHook webHook, CancellationToken cancellationToken) { if (webHook.Filters == null || webHook.Filters.Count == 0) { throw new ArgumentException("WebHooks need to target at least one trigger. Wildcard is not allowed."); } var triggers = WebHookTriggerProvider.GetAvailableTriggers(); var errors = new List <string>(); foreach (var filter in webHook.Filters) { if (!triggers.ContainsKey(filter.Trigger)) { errors.Add($" - Trigger '{filter.Trigger}' is not valid."); continue; } } if (errors.Count != 0) { throw new ArgumentException("WebHooks filters are incorrect :" + Environment.NewLine + string.Join(Environment.NewLine, errors)); } return(Task.CompletedTask); }
/// <summary> /// Initializes a new instance of the <see cref="WebHookWorkItem"/> class. /// </summary> public WebHookWorkItem(IWebHookNotification notification, IWebHook webHook) { Notification = notification ?? throw new ArgumentNullException(nameof(notification)); WebHook = webHook ?? throw new ArgumentNullException(nameof(webHook)); Id = Guid.NewGuid(); Timestamp = DateTime.UtcNow; }
public CriarPaymentHandler(IUnitOfWork unitOfWork, IMapper mapper, IWebHook webHook, IPayAtOperatorService payAtOperatorService) { _unitOfWork = unitOfWork; _mapper = mapper; _payAtOperatorService = payAtOperatorService; _webHook = webHook; }
/// <summary> /// Id validation /// </summary> /// <param name="webHook"></param> /// <param name="cancellationToken"></param> /// <returns></returns> protected virtual Task VerifyIdAsync(IWebHook webHook, CancellationToken cancellationToken) { if (webHook.Id == default) { webHook.Id = Guid.NewGuid(); } return(Task.CompletedTask); }
/// <inheritdoc /> public virtual async Task ValidateAsync(IWebHook webHook, CancellationToken cancellationToken = default) { if (webHook == null) { throw new ArgumentNullException(nameof(webHook)); } await VerifyIdAsync(webHook, cancellationToken); await VerifySecretAsync(webHook, cancellationToken); await VerifyFiltersAsync(webHook, cancellationToken); await VerifyCallbackAsync(webHook, cancellationToken); }
/// <summary>Initializes a new instance of the <see cref="WebHook"/> class.</summary> public WebHook(IWebHook webHook) { if (webHook == null) { throw new ArgumentNullException(nameof(webHook)); } Id = webHook.Id; Callback = webHook.Callback; Secret = webHook.Secret; IsPaused = webHook.IsPaused; Filters = webHook.Filters.Select(f => new WebHookFilter(f)).ToList(); }
/// <summary> /// Secret validation /// </summary> /// <param name="webHook"></param> /// <param name="cancellationToken"></param> /// <returns></returns> /// <exception cref="ArgumentException">Secret is not a 64 characters string</exception> protected virtual Task VerifySecretAsync(IWebHook webHook, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(webHook.Secret)) { webHook.Secret = GetUniqueKey(64); return(Task.CompletedTask); } if (webHook.Secret.Length != 64) { throw new ArgumentException("WebHooks secret needs to be set to a 64 characters string."); } return(Task.CompletedTask); }
/// <summary> /// Callback validation. If noecho is used, the given url should not be called. /// The url is tested by sending a GET request containing a echo query parameter that should be echoed. /// </summary> /// <param name="webHook"></param> /// <param name="cancellationToken"></param> /// <returns></returns> /// <exception cref="ArgumentException">Callback is invalid, not an http(s) url, of echo procedure failed</exception> protected virtual async Task VerifyCallbackAsync(IWebHook webHook, CancellationToken cancellationToken) { if (webHook.Callback == null) { throw new ArgumentException("WebHooks callback needs to be set."); } if (!(webHook.Callback.IsAbsoluteUri && ValidSchemes.Contains(webHook.Callback.Scheme))) { throw new ArgumentException("WebHooks callback needs to be a valid http(s) absolute Uri."); } var query = HttpUtility.ParseQueryString(webHook.Callback.Query); if (query["noecho"] != null) { Logger.LogInformation($"Webhook {webHook.Id} does not allow url verification (noecho query parameter has been found)."); return; } try { var expectedResult = Guid.NewGuid(); var echoUri = new UriBuilder(webHook.Callback) { Query = "echo=" + expectedResult }; var response = await HttpClient.GetStringAsync(echoUri.Uri); if (Guid.TryParse(response, out var responseResult) && responseResult == expectedResult) { return; } throw new ArgumentException($"WebHook {webHook.Id} callback verification failed. Response is incorrect: {response}.{Environment.NewLine}To cancel callback verification, add `noecho` as a query parameter."); } catch (Exception e) { var message = $"WebHook {webHook.Id} callback verification failed: {e.Message}.{Environment.NewLine}To cancel callback verification, add `noecho` as a query parameter."; Logger.LogError(e, message); throw new ArgumentException(message); } }
/// <inheritdoc /> public bool Matches(IWebHook webHook, IWebHookNotification notification) { if (webHook == null) { throw new ArgumentNullException(nameof(webHook)); } if (notification == null) { throw new ArgumentNullException(nameof(notification)); } if (webHook.Filters == null || webHook.Filters.Count == 0) { return(true); } return(webHook.Filters .Where(f => IsTriggerMatching(f.Trigger, notification.TriggerId)) .OrderBy(f => f.Parameters == null ? 0 : f.Parameters.Count) .Any(f => f.Parameters == null || f.Parameters.Count == 0 || f.Parameters.All(kvp => IsPayloadMatchingParameter(kvp.Key, kvp.Value, notification.Payload)))); }
/// <summary> /// Apply signature to the HttpRequestMessage /// </summary> /// <param name="webHook"></param> /// <param name="request"></param> /// <param name="serializedBody"></param> protected virtual void SignRequest(IWebHook webHook, HttpRequestMessage request, string serializedBody) { var signature = SignatureService.GetSignature(webHook.Secret, serializedBody); request.Headers.Add(SignatureHeader, signature); }
/// <inheritdoc /> /// <exception cref="ArgumentException">Id, callback, secret or filters is <see langword="null" /></exception> public async Task <WebHookRegistrationStoreResult> InsertWebHookAsync(IPrincipal user, IWebHook webHook, CancellationToken cancellationToken = default) { if (webHook == null) { throw new ArgumentNullException(nameof(webHook)); } if (webHook.Id == default) { throw new ArgumentException("WebHook id needs to be set by client."); } if (webHook.Callback == null) { throw new ArgumentException("WebHook callback needs to be set."); } if (webHook.Secret == null) { throw new ArgumentException("WebHook secret needs to be set."); } if (webHook.Filters == null) { throw new ArgumentException("WebHook filters needs to be set."); } var key = await _idGetter.GetPrincipalIdAsync(user, cancellationToken); var dbWebHook = new WebHook { PrincipalId = key, Callback = webHook.Callback.ToString(), ProtectedSecret = _secretProtector.Protect(webHook.Secret), Filters = webHook.Filters.Select(f => new WebHookFilter { Trigger = f.Trigger }).ToList() }; try { await _context.SaveAsync(dbWebHook); return(WebHookRegistrationStoreResult.Success); } catch (Exception e) { _logger.LogError(e, $"WebHook {dbWebHook.Id} insertion failed : {e.Message}"); return(WebHookRegistrationStoreResult.InternalError); } }
/// <inheritdoc /> public async Task <WebHookRegistrationStoreResult> UpdateWebHookAsync(IPrincipal user, IWebHook webHook, CancellationToken cancellationToken = default) { if (webHook == null) { throw new ArgumentNullException(nameof(webHook)); } var key = await _idGetter.GetPrincipalIdAsync(user, cancellationToken); var filterDefinition = Builders <WebHook> .Filter.Where(w => w.PrincipalId == key && w.Id == webHook.Id); var dbWebHook = await _context.Collection .Find(filterDefinition) .FirstOrDefaultAsync(cancellationToken); if (dbWebHook == null) { return(WebHookRegistrationStoreResult.NotFound); } dbWebHook.IsPaused = webHook.IsPaused; if (webHook.Callback != null) { dbWebHook.Callback = webHook.Callback.ToString(); } if (!string.IsNullOrEmpty(webHook.Secret)) { dbWebHook.ProtectedSecret = _secretProtector.Protect(webHook.Secret); } if (webHook.Filters != null) { dbWebHook.Filters = webHook.Filters.Select(f => new WebHookFilter { Trigger = f.Trigger }).ToList(); } try { await _context.SaveAsync(dbWebHook); return(WebHookRegistrationStoreResult.Success); } catch (Exception e) { _logger.LogError(e, $"WebHook {dbWebHook.Id} update failed : {e.Message}"); return(WebHookRegistrationStoreResult.InternalError); } }
/// <inheritdoc /> public async Task <WebHookRegistrationStoreResult> UpdateWebHookAsync(IPrincipal user, IWebHook webHook, CancellationToken cancellationToken = default) { if (webHook == null) { throw new ArgumentNullException(nameof(webHook)); } var key = await _idGetter.GetPrincipalIdAsync(user, cancellationToken); var dbWebHook = await _context.WebHooks .Where(w => w.PrincipalId == key && w.Id == webHook.Id) .Include(w => w.Filters) .FirstOrDefaultAsync(cancellationToken); if (dbWebHook == null) { return(WebHookRegistrationStoreResult.NotFound); } dbWebHook.IsPaused = webHook.IsPaused; if (webHook.Callback != null) { dbWebHook.ProtectedCallback = _secretProtector.Protect(webHook.Callback.ToString()); } if (!string.IsNullOrEmpty(webHook.Secret)) { dbWebHook.ProtectedSecret = _secretProtector.Protect(webHook.Secret); } if (webHook.Filters != null) { _context.RemoveRange(dbWebHook.Filters); dbWebHook.Filters = webHook.Filters.Select(f => new WebHookFilter { TriggerId = f.TriggerId, Parameters = f.Parameters == null ? null : new Dictionary <string, object>(f.Parameters) }).ToList(); } try { await _context.SaveChangesAsync(cancellationToken); return(WebHookRegistrationStoreResult.Success); } catch (Exception e) { _logger.LogError(e, $"WebHook {dbWebHook.Id} update failed : {e.Message}"); return(WebHookRegistrationStoreResult.InternalError); } }