示例#1
0
        /// <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);
        }
示例#2
0
 /// <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;
 }
示例#3
0
 public CriarPaymentHandler(IUnitOfWork unitOfWork, IMapper mapper, IWebHook webHook, IPayAtOperatorService payAtOperatorService)
 {
     _unitOfWork           = unitOfWork;
     _mapper               = mapper;
     _payAtOperatorService = payAtOperatorService;
     _webHook              = webHook;
 }
示例#4
0
 /// <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);
 }
示例#5
0
        /// <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);
        }
示例#6
0
        /// <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();
        }
示例#7
0
        /// <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);
        }
示例#8
0
        /// <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);
            }
        }
示例#9
0
        /// <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))));
        }
示例#10
0
        /// <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);
        }
示例#11
0
        /// <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);
            }
        }
示例#12
0
        /// <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);
            }
        }
示例#13
0
        /// <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);
            }
        }