/// <inheritdoc /> public HttpVerb SelectHttpVerb(WebhookConfig webhookConfig, string payload) { //build the uri from the routes first var routingRules = webhookConfig.WebhookRequestRules.FirstOrDefault(l => l.Routes.Any()); if (routingRules != null) { if (routingRules.Source.Location == Location.Body) { var path = routingRules.Source.Path; var value = ModelParser.ParsePayloadPropertyAsString(path, payload); if (string.IsNullOrWhiteSpace(value)) { throw new ArgumentNullException(nameof(path), "routing path value in message payload is null or empty"); } //selects the route based on the value found in the payload of the message foreach (var rules in webhookConfig.WebhookRequestRules.Where(r => r.Routes.Any())) { var route = rules.Routes.FirstOrDefault(r => r.Selector.Equals(value, StringComparison.OrdinalIgnoreCase)); if (route != null) { return(route.HttpVerb); } throw new Exception("route http verb mapping/selector not found between config and the properties on the domain object"); } } } return(webhookConfig.HttpVerb); }
/// <inheritdoc /> public HttpVerb SelectHttpVerb(WebhookConfig webhookConfig, string payload) { //build the uri from the routes first var rules = webhookConfig.WebhookRequestRules.FirstOrDefault(r => r.Destination.RuleAction == RuleAction.Route); if (rules == null) { return(webhookConfig.HttpVerb); } if (rules.Source.Location != Location.Body) { return(webhookConfig.HttpVerb); } var value = ModelParser.ParsePayloadPropertyAsString(rules.Source.Path, payload); if (string.IsNullOrWhiteSpace(value)) { throw new ArgumentNullException(nameof(rules.Source.Path), "routing path value in message payload is null or empty"); } var route = rules.Routes.FirstOrDefault(r => r.Selector.Equals(value, StringComparison.OrdinalIgnoreCase)); if (route == null) { throw new Exception("route http verb mapping/selector not found between config and the properties on the domain object"); } return(route.HttpVerb); }
/// <summary> /// /// </summary> /// <param name="config"></param> /// <returns></returns> public HttpClient Get(WebhookConfig config) { if (config == null) { throw new ArgumentNullException(nameof(config)); } var uri = new Uri(config.Uri); if (_httpClients.TryGetValue(uri.Host.ToLowerInvariant(), out var httpClient)) { return(httpClient); } httpClient = new HttpClient { Timeout = config.Timeout }; var result = _httpClients.TryAdd(uri.Host.ToLowerInvariant(), httpClient); if (!result) { throw new ArgumentNullException(nameof(httpClient), $"HttpClient for {uri.Host} could not be added to the dictionary of http clients"); } return(httpClient); }
public Webhook(string name, bool active, ArrayList events, WebhookConfig config) { this.name = name; this.active = active; this.events = events; this.config = config; }
/// <summary> /// Parse the auth scheme from config to concrete type /// </summary> /// <param name="route"></param> /// <param name="configurationSection"></param> /// <param name="path"></param> public static void ParseAuthScheme(WebhookConfig route, IConfiguration configurationSection, string path) { if (route.AuthenticationConfig.Type == AuthenticationType.Basic) { var basicAuthenticationConfig = new BasicAuthenticationConfig { Username = configurationSection[path + ":username"], Password = configurationSection[path + ":password"] }; route.AuthenticationConfig = basicAuthenticationConfig; } if (route.AuthenticationConfig.Type == AuthenticationType.OIDC) { route.AuthenticationConfig = ParseOidcAuthenticationConfig(configurationSection.GetSection(path)); } if (route.AuthenticationConfig.Type != AuthenticationType.Custom) { return; } route.AuthenticationConfig = ParseOidcAuthenticationConfig(configurationSection.GetSection(path)); route.AuthenticationConfig.Type = AuthenticationType.Custom; }
public async Task ChecksHttpGetVerb(WebhookConfig config, HttpMethod httpMethod, string payload, HttpStatusCode expectedResponseCode, string expectedResponseBody) { var mockHttp = new MockHttpMessageHandler(); var request = mockHttp.When(httpMethod, config.Uri) .Respond(expectedResponseCode, "application/json", expectedResponseBody); var mockBigBrother = new Mock <IBigBrother>(); var httpClients = new Dictionary <string, HttpClient> { { new Uri(config.Uri).Host, mockHttp.ToHttpClient() } }; var httpClientBuilder = new HttpClientFactory(httpClients); var requestBuilder = new RequestBuilder(); var requestLogger = new RequestLogger(mockBigBrother.Object); var genericWebhookHandler = new GenericWebhookHandler( httpClientBuilder, new Mock <IAuthenticationHandlerFactory>().Object, requestBuilder, requestLogger, mockBigBrother.Object, config); await genericWebhookHandler.CallAsync(new MessageData(payload, "TestType", "subA", "service") { CorrelationId = Guid.NewGuid().ToString() }, new Dictionary <string, object>(), _cancellationToken); Assert.Equal(1, mockHttp.GetMatchCount(request)); }
public async Task GetTokenProvider(WebhookConfig config, IAuthenticationHandler expectedHandler) { var factory = new AuthenticationHandlerFactory(new HttpClientFactory(), new Mock <IBigBrother>().Object); var handler = await factory.GetAsync(config, CancellationToken.None); Assert.Equal(expectedHandler.GetType(), handler.GetType()); }
public async Task NoAuthentication(WebhookConfig config) { var factory = new AuthenticationHandlerFactory(new HttpClientFactory(), new Mock <IBigBrother>().Object); var handler = await factory.GetAsync(config, CancellationToken.None); Assert.Null(handler); }
public void HttpVerbSelectionTests( WebhookConfig config, string sourcePayload, HttpVerb expectedVerb) { var selectedVerb = new RequestBuilder().SelectHttpVerb(config, sourcePayload); Assert.Equal(expectedVerb, selectedVerb); }
public WebhookController( ILogger <WebhookController> logger, IOptions <WebhookConfig> webhookConfig, IWebhookService webhookService) { _logger = logger; _webhookConfig = webhookConfig.Value; _webhookService = webhookService; }
public void AuthenticationSchemeSelectionTests( WebhookConfig config, string sourcePayload, AuthenticationType expectedAuthenticationType) { var authenticationConfig = new RequestBuilder().GetAuthenticationConfig(config, sourcePayload); Assert.Equal(expectedAuthenticationType, authenticationConfig.AuthenticationConfig.Type); }
public void PayloadConstructionTests( WebhookConfig config, string sourcePayload, Dictionary <string, object> data, string expectedPayload) { var requestPayload = new RequestBuilder().BuildPayload(config, sourcePayload, data); Assert.Equal(expectedPayload, requestPayload); }
/// <inheritdoc /> public Uri BuildUri(WebhookConfig config, string payload) { if (config == null) { throw new ArgumentNullException(nameof(config)); } var uri = config.Uri; //build the uri from the routes first var rules = config.WebhookRequestRules.FirstOrDefault(r => r.Destination.RuleAction == RuleAction.Route); if (rules != null) { var selector = string.Empty; if (rules.Source.Location == Location.Body) { selector = ModelParser.ParsePayloadPropertyAsString(rules.Source.Path, payload); } if (string.IsNullOrWhiteSpace(selector)) { throw new ArgumentNullException(nameof(rules.Source.Path), "routing path value in message payload is null or empty"); } //selects the route based on the value found in the payload of the message var route = rules.Routes.FirstOrDefault(r => r.Selector.Equals(selector, StringComparison.OrdinalIgnoreCase)); if (route == null) { throw new Exception("route mapping/selector not found between config and the properties on the domain object"); } uri = route.Uri; } //after route has been selected then select the identifier for the RESTful URI if applicable var uriRules = config.WebhookRequestRules.FirstOrDefault(l => l.Destination.Location == Location.Uri); if (uriRules == null) { return(new Uri(uri)); } if (uriRules.Source.Location != Location.Body) { return(new Uri(uri)); } var parameter = ModelParser.ParsePayloadPropertyAsString(uriRules.Source.Path, payload); uri = CombineUriAndResourceId(uri, parameter); return(new Uri(uri)); }
public async Task ExecuteHappyPathRawContract() { var(messageData, metaData) = EventHandlerTestHelper.CreateMessageDataPayload(); var config = new WebhookConfig { Uri = "http://localhost/webhook", HttpMethod = HttpMethod.Put, EventType = "Event1", AuthenticationConfig = new AuthenticationConfig(), WebhookRequestRules = new List <WebhookRequestRule> { new WebhookRequestRule { Source = new ParserLocation { Path = "OrderCode" }, Destination = new ParserLocation { Location = Location.Uri } } } }; var mockHttp = new MockHttpMessageHandler(); var webhookRequest = mockHttp.When(HttpMethod.Put, $"{config.Uri}/{metaData["OrderCode"]}") .WithContentType("application/json", messageData.Payload) .Respond(HttpStatusCode.OK, "application/json", string.Empty); var mockBigBrother = new Mock <IBigBrother>(); var httpClients = new Dictionary <string, HttpClient> { { new Uri(config.Uri).Host, mockHttp.ToHttpClient() } }; var httpClientBuilder = new HttpClientFactory(httpClients); var requestBuilder = new RequestBuilder(); var requestLogger = new RequestLogger(mockBigBrother.Object); var genericWebhookHandler = new GenericWebhookHandler( httpClientBuilder, new Mock <IAuthenticationHandlerFactory>().Object, requestBuilder, requestLogger, mockBigBrother.Object, config); await genericWebhookHandler.CallAsync(messageData, new Dictionary <string, object>(), _cancellationToken); Assert.Equal(1, mockHttp.GetMatchCount(webhookRequest)); }
public GenericWebhookHandler( IAcquireTokenHandler acquireTokenHandler, IRequestBuilder requestBuilder, IBigBrother bigBrother, HttpClient client, WebhookConfig webhookConfig) { _client = client; AcquireTokenHandler = acquireTokenHandler; BigBrother = bigBrother; RequestBuilder = requestBuilder; WebhookConfig = webhookConfig; }
public void CanGetHttpClient(WebhookConfig config) { var mockHttp = new MockHttpMessageHandler(); var httpClients = new Dictionary <string, HttpClient> { { new Uri(config.Uri).Host, mockHttp.ToHttpClient() } }; var httpClientBuilder = new HttpClientFactory(httpClients); var httpClient = httpClientBuilder.Get(config); Assert.NotNull(httpClient); }
/// <summary> /// /// </summary> /// <param name="config"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public async Task <IAuthenticationHandler> GetAsync(WebhookConfig config, CancellationToken cancellationToken) { if (config == null) { throw new ArgumentNullException(nameof(config)); } var key = config.Uri; switch (config.AuthenticationConfig.Type) { case AuthenticationType.None: return(null); case AuthenticationType.Basic: await EnterSemaphore(() => { _handlers.TryAdd(key, new BasicAuthenticationHandler(config.AuthenticationConfig)); }, key, cancellationToken); break; case AuthenticationType.OIDC: await EnterSemaphore(() => { _handlers.TryAdd(key, new OidcAuthenticationHandler(_httpClientFactory, config.AuthenticationConfig, _bigBrother)); }, key, cancellationToken); break; case AuthenticationType.Custom: //todo hack for now until we move this out of here and into an integration layer //todo if this is custom it should be another webhook which calls out to another place, this place gets a token on CH's behalf and then adds this into subsequent webhook requests. await EnterSemaphore(() => { _handlers.TryAdd(key, new MmAuthenticationHandler(_httpClientFactory, config.AuthenticationConfig, _bigBrother)); }, key, cancellationToken); break; default: throw new ArgumentOutOfRangeException(nameof(config.AuthenticationConfig.Type), $"unknown configuration type of {config.AuthenticationConfig.Type}"); } _handlers.TryGetValue(key, out var newHandler); return(newHandler); }
public GenericWebhookHandler( IHttpClientFactory httpClientFactory, IAuthenticationHandlerFactory authenticationHandlerFactory, IRequestBuilder requestBuilder, IRequestLogger requestLogger, IBigBrother bigBrother, WebhookConfig webhookConfig) { BigBrother = bigBrother; RequestBuilder = requestBuilder; _requestLogger = requestLogger; WebhookConfig = webhookConfig; _authenticationHandlerFactory = authenticationHandlerFactory; HttpClientFactory = httpClientFactory; }
public async Task ChecksHttpGetVerb(WebhookConfig config, HttpMethod httpMethod, string payload, HttpStatusCode expectedResponseCode, string expectedResponseBody) { var mockHttp = new MockHttpMessageHandler(); var request = mockHttp.When(httpMethod, config.Uri) .Respond(expectedResponseCode, "application/json", expectedResponseBody); var genericWebhookHandler = new GenericWebhookHandler( new Mock <IAcquireTokenHandler>().Object, new RequestBuilder(), new Mock <IBigBrother>().Object, mockHttp.ToHttpClient(), config); await genericWebhookHandler.Call(new MessageData { Payload = payload }); Assert.Equal(1, mockHttp.GetMatchCount(request)); }
/// <summary> /// Creates a list of unique endpoints which are used for authentication sharing and pooling /// </summary> /// <param name="webhookConfig"></param> /// <param name="endpointList"></param> /// <param name="configurationSection"></param> /// <param name="path"></param> public static void AddEndpoints(WebhookConfig webhookConfig, IDictionary <string, WebhookConfig> endpointList, IConfiguration configurationSection, string path) { if (webhookConfig == null) { throw new ArgumentNullException(nameof(webhookConfig)); } if (endpointList == null) { throw new ArgumentNullException(nameof(endpointList)); } if (configurationSection == null) { throw new ArgumentNullException(nameof(configurationSection)); } //creates a list of endpoints so they can be shared for authentication and http pooling if (string.IsNullOrWhiteSpace(webhookConfig.Uri)) { if (!webhookConfig.WebhookRequestRules.Any(r => r.Routes.Any())) { return; } for (var i = 0; i < webhookConfig.WebhookRequestRules.Count; i++) { var webhookRequestRule = webhookConfig.WebhookRequestRules[i]; for (var y = 0; y < webhookRequestRule.Routes.Count; y++) { var route = webhookRequestRule.Routes[y]; if (string.IsNullOrWhiteSpace(route.Uri)) { continue; } var authPath = $"{path}:webhookrequestrules:{i + 1}:routes:{y + 1}:authenticationconfig"; ParseAuthScheme(route, configurationSection, authPath); AddToDictionarySafely(endpointList, route); } } } else { AddToDictionarySafely(endpointList, webhookConfig); } }
public void init() { WebhookController webhookService = new Controllers.WebhookController(); webhookService.Log("Starting Application..."); ArrayList eventList = new ArrayList(); eventList.Add("deployment"); WebhookConfig config = new WebhookConfig("https://cd4dc1d6.ngrok.io/api/WebhookController/event", "json", "secret", false); Webhook webhook = new Webhook("web", true, eventList, config); webhookService.Log("Trying to register Webhook..."); webhookService.RegisterWebhook("https://api.github.com/repos/hoedlale16/campus02.ieg.umfrageplattform/hooks", webhook); //webhookService.RegisterWebhook("https://api.github.com/repos/MrPink1992/web/hooks", webhook); webhookService.Log("Webhook registration finished"); }
/// <inheritdoc /> public WebHookHeaders GetHttpHeaders(WebhookConfig webhookConfig, MessageData messageData) { if (webhookConfig == null) { throw new ArgumentNullException(nameof(webhookConfig)); } if (messageData == null) { throw new ArgumentNullException(nameof(messageData)); } var headers = new WebHookHeaders(); headers.AddContentHeader(Constants.Headers.ContentType, webhookConfig.ContentType); headers.AddRequestHeader(Constants.Headers.CorrelationId, messageData.CorrelationId); headers.AddRequestHeader(Constants.Headers.EventDeliveryId, messageData.CorrelationId); headers.AddRequestHeader(Constants.Headers.EventType, webhookConfig.EventType); return(headers); }
public async Task ExecuteHappyPath() { var(messageData, metaData) = EventHandlerTestHelper.CreateMessageDataPayload(); var config = new WebhookConfig { Uri = "http://localhost/webhook", HttpVerb = HttpVerb.Put, AuthenticationConfig = new AuthenticationConfig(), WebhookRequestRules = new List <WebhookRequestRule> { new WebhookRequestRule { Source = new ParserLocation { Path = "OrderCode" }, Destination = new ParserLocation { Location = Location.Uri } } } }; var mockHttp = new MockHttpMessageHandler(); var webhookRequest = mockHttp.When(HttpMethod.Put, $"{config.Uri}/{metaData["OrderCode"]}") .WithContentType("application/json", messageData.Payload) .Respond(HttpStatusCode.OK, "application/json", string.Empty); var genericWebhookHandler = new GenericWebhookHandler( new Mock <IAcquireTokenHandler>().Object, new RequestBuilder(), new Mock <IBigBrother>().Object, mockHttp.ToHttpClient(), config); await genericWebhookHandler.Call(messageData); Assert.Equal(1, mockHttp.GetMatchCount(webhookRequest)); }
public async Task OnLoad(IServer server) { _server = server; _config = await _configManager.LoadConfig <PluginConfig>(); _webhook = await _configManager.LoadConfig <WebhookConfig>(); if (!_config.Enabled) { return; } SetStatusMessage(); _statusTimer = new Timer(_config.BotStatus.Interval); _statusTimer.Elapsed += async(sender, e) => await UpdateStatus(); _statusTimer.Start(); _client = new DiscordClient(new DiscordConfiguration { Token = _config.Token, TokenType = TokenType.Bot }); _client.Ready += Discord_Ready; if (_server != null && _config.ChatSync.Enabled) { _client.MessageCreated += Discord_MessageCreated; } _ = Task.Run(async() => { await _client.ConnectAsync(_discordStatus); }); Logger.Log($"{Info.Name} v{Info.Version} loaded!"); }
private string WrapPayload(string originalPayload, WebhookConfig webhookConfig, MessageData messageData) { var source = new WrapperPayloadContract { CallbackType = messageData.IsDlq ? CallbackTypeEnum.DeliveryFailure : CallbackTypeEnum.Callback, EventType = webhookConfig.EventType, MessageId = messageData.CorrelationId, StatusCode = null, //this is never specified for non callback Payload = JObject.Parse(originalPayload), StatusUri = null //for now }; using (var sw = new StringWriter()) { using (var writer = new JsonTextWriter(sw)) { JsonSerializer.CreateDefault().Serialize(writer, source); writer.Flush(); return(sw.ToString()); } } }
/// <inheritdoc /> public WebhookConfig SelectWebhookConfig(WebhookConfig webhookConfig, string payload) { if (webhookConfig == null) { throw new ArgumentNullException(nameof(webhookConfig)); } var rules = webhookConfig.WebhookRequestRules.FirstOrDefault(r => r.Destination.RuleAction == RuleAction.Route); if (rules == null) { return(webhookConfig); } var selector = string.Empty; if (rules.Source.Location == Location.Body) { selector = ModelParser.ParsePayloadPropertyAsString(rules.Source.Path, payload); } if (string.IsNullOrWhiteSpace(selector)) { throw new ArgumentNullException(nameof(rules.Source.Path), "routing path value in message payload is null or empty"); } //selects the route based on the value found in the payload of the message var route = rules.Routes.FirstOrDefault(r => r.Selector.Equals(selector, StringComparison.OrdinalIgnoreCase)); if (route == null) { throw new Exception("route mapping/selector not found between config and the properties on the domain object"); } return(route); }
/// <summary> /// Sends an HTTP Request with the requested parameters /// </summary> /// <param name="url"></param> /// <param name="method"></param> /// <param name="contentType"></param> /// <param name="body"></param> /// <returns> A named Tuple with Success boolean and int StatusCode of the HTTP Request </returns> public static async Task <WebhookResult> SendRequest(WebhookConfig config, ManagedCertificate item, bool forSuccess) { using (var client = new HttpClient()) { client.DefaultRequestHeaders.Add("User-Agent", Management.Util.GetUserAgent()); HttpRequestMessage message; var url = ParseValues(config.Url, item.RequestConfig, forSuccess, true); switch (config.Method) { case METHOD_GET: message = new HttpRequestMessage(HttpMethod.Get, url); break; case METHOD_POST: message = new HttpRequestMessage(HttpMethod.Post, url) { Content = new StringContent( ParseValues(!string.IsNullOrEmpty(config.ContentBody) ? config.ContentBody : DEFAULT_BODY, item.RequestConfig, forSuccess, false), Encoding.UTF8, string.IsNullOrEmpty(config.ContentType) ? "application/json" : config.ContentType ) }; break; default: throw new ArgumentException("Method must be GET or POST", "method"); } var resp = await client.SendAsync(message); return(new WebhookResult(resp.IsSuccessStatusCode, (int)resp.StatusCode)); } }
protected async Task AddAuthenticationHeaderAsync(CancellationToken cancellationToken, WebhookConfig config, WebHookHeaders webHookHeaders) { if (config.AuthenticationConfig.Type != AuthenticationType.None) { var acquireTokenHandler = await _authenticationHandlerFactory.GetAsync(config, cancellationToken); var result = await acquireTokenHandler.GetTokenAsync(cancellationToken); webHookHeaders.AddRequestHeader(Constants.Headers.Authorization, result); } }
/// <summary> /// Safe adds to the dictionary if it does not already exist /// </summary> /// <param name="endpointList"></param> /// <param name="rule"></param> public static void AddToDictionarySafely(IDictionary <string, WebhookConfig> endpointList, WebhookConfig rule) { var uri = new Uri(rule.Uri); if (!endpointList.ContainsKey(uri.Host.ToLowerInvariant())) { endpointList.Add(uri.Host.ToLowerInvariant(), rule); } }
/// <inheritdoc /> public string BuildPayload(WebhookConfig config, string sourcePayload, IDictionary <string, object> metadata = null) { var rules = config.WebhookRequestRules.Where(l => l.Destination.Location == Location.Body).ToList(); if (!rules.Any()) { return(sourcePayload); } //Any replace action replaces the payload var replaceRule = rules.FirstOrDefault(r => r.Destination.RuleAction == RuleAction.Replace); if (replaceRule != null) { var destinationPayload = ModelParser.ParsePayloadProperty(replaceRule.Source, sourcePayload, replaceRule.Destination.Type); if (rules.Count <= 1) { return(destinationPayload.ToString(Formatting.None)); } } if (metadata == null) { metadata = new Dictionary <string, object>(); } JContainer payload = new JObject(); foreach (var rule in rules) { if (rule.Destination.RuleAction != RuleAction.Add) { continue; } if (rule.Source.RuleAction == RuleAction.Route) { continue; } object value; switch (rule.Source.Type) { case DataType.String: case DataType.Property: case DataType.Model: value = ModelParser.ParsePayloadProperty(rule.Source, sourcePayload, rule.Destination.Type); break; case DataType.HttpContent: metadata.TryGetValue("HttpResponseContent", out var httpContent); value = ModelParser.GetJObject(httpContent, rule.Destination.Type); break; case DataType.HttpStatusCode: metadata.TryGetValue("HttpStatusCode", out var httpStatusCode); value = ModelParser.GetJObject(httpStatusCode, rule.Destination.Type); break; default: throw new ArgumentOutOfRangeException(); } if (string.IsNullOrWhiteSpace(rule.Destination.Path)) { payload = (JContainer)value; continue; } payload.Add(new JProperty(rule.Destination.Path, value)); } return(payload.ToString(Formatting.None)); }