diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 0af49f955..f8d608a27 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -397,7 +397,6 @@ services: webhooks.client: environment: - - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://0.0.0.0:80 - Token=6168DB8D-DC58-4094-AF24-483278923590 # Webhooks are registered with this token (any value is valid) but the client won't check it - IdentityUrl=http://10.0.75.1:5105 diff --git a/src/Services/Webhooks/Webhooks.API/Controllers/WebhooksController.cs b/src/Services/Webhooks/Webhooks.API/Controllers/WebhooksController.cs index 4c8c986f0..23160f92d 100644 --- a/src/Services/Webhooks/Webhooks.API/Controllers/WebhooksController.cs +++ b/src/Services/Webhooks/Webhooks.API/Controllers/WebhooksController.cs @@ -38,7 +38,7 @@ namespace Webhooks.API.Controllers } [Authorize] - [HttpGet("{id:int}", Name = "Get")] + [HttpGet("{id:int}")] [ProducesResponseType(typeof(WebhookSubscription), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task GetByUserAndId(int id) @@ -82,7 +82,7 @@ namespace Webhooks.API.Controllers _dbContext.Add(subscription); await _dbContext.SaveChangesAsync(); - return CreatedAtAction("Get", new { id = subscription.Id }); + return CreatedAtAction("GetByUserAndId", new { id = subscription.Id }, subscription); } else { diff --git a/src/Services/Webhooks/Webhooks.API/Services/GrantUrlTesterService.cs b/src/Services/Webhooks/Webhooks.API/Services/GrantUrlTesterService.cs index 4653580ce..c2e3315fd 100644 --- a/src/Services/Webhooks/Webhooks.API/Services/GrantUrlTesterService.cs +++ b/src/Services/Webhooks/Webhooks.API/Services/GrantUrlTesterService.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Extensions.Logging; +using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; @@ -9,19 +10,30 @@ namespace Webhooks.API.Services class GrantUrlTesterService : IGrantUrlTesterService { private readonly IHttpClientFactory _clientFactory; - public GrantUrlTesterService(IHttpClientFactory factory) + private readonly ILogger _logger; + public GrantUrlTesterService(IHttpClientFactory factory, ILogger logger) { _clientFactory = factory; + _logger = logger; } public async Task TestGrantUrl(string url, string token) { var client = _clientFactory.CreateClient("GrantClient"); - var msg = new HttpRequestMessage(HttpMethod.Options, url); msg.Headers.Add("X-eshop-whtoken", token); - var response = await client.SendAsync(msg); - return response.IsSuccessStatusCode; + _logger.LogTrace($"Sending the OPTIONS message to {url} with token {token ?? string.Empty}"); + try + { + var response = await client.SendAsync(msg); + _logger.LogInformation($"Response code is {response.StatusCode} for url {url}"); + return response.IsSuccessStatusCode; + } + catch (Exception ex) + { + _logger.LogWarning($"Exception {ex.GetType().Name} when sending OPTIONS request. Url can't be granted."); + return false; + } } } } diff --git a/src/Web/WebhookClient/.dockerignore b/src/Web/WebhookClient/.dockerignore new file mode 100644 index 000000000..0f6ecb9ba --- /dev/null +++ b/src/Web/WebhookClient/.dockerignore @@ -0,0 +1 @@ +!wwwroot \ No newline at end of file diff --git a/src/Web/WebhookClient/Controllers/WebhooksReceivedController.cs b/src/Web/WebhookClient/Controllers/WebhooksReceivedController.cs new file mode 100644 index 000000000..849f5092f --- /dev/null +++ b/src/Web/WebhookClient/Controllers/WebhooksReceivedController.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using WebhookClient.Models; + +namespace WebhookClient.Controllers +{ + [ApiController] + [Route("webhook-received")] + public class WebhooksReceivedController : Controller + { + + private readonly Settings _settings; + + public WebhooksReceivedController(IOptions settings) + { + _settings = settings.Value; + } + + public IActionResult NewWebhook(WebhookData hook) + { + var header = Request.Headers[HeaderNames.WebHookCheckHeader]; + var token = header.FirstOrDefault(); + if (!_settings.ValidateToken || _settings.Token == token) + { + var received = HttpContext.Session.Get>(SessionKeys.HooksKey)?.ToList() ?? new List(); + received.Add(new WebHookReceived() + { + Data = hook.Payload, + When = hook.When, + Token = token + }); + return Ok(); + } + + return BadRequest(); + } + } +} diff --git a/src/Web/WebhookClient/Models/WebHookReceived.cs b/src/Web/WebhookClient/Models/WebHookReceived.cs index 8affb48f0..80cc46c4b 100644 --- a/src/Web/WebhookClient/Models/WebHookReceived.cs +++ b/src/Web/WebhookClient/Models/WebHookReceived.cs @@ -9,5 +9,7 @@ namespace WebhookClient.Models { public DateTime When { get; set; } public string Data { get; set; } + + public string Token { get; set; } } } diff --git a/src/Web/WebhookClient/Models/WebhookData.cs b/src/Web/WebhookClient/Models/WebhookData.cs new file mode 100644 index 000000000..a20d46635 --- /dev/null +++ b/src/Web/WebhookClient/Models/WebhookData.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace WebhookClient.Models +{ + public class WebhookData + { + public DateTime When { get; } + + public string Payload { get; } + + public string Type { get; } + } +} diff --git a/src/Web/WebhookClient/Pages/Index.cshtml b/src/Web/WebhookClient/Pages/Index.cshtml index bc2f88fa0..0118e949b 100644 --- a/src/Web/WebhookClient/Pages/Index.cshtml +++ b/src/Web/WebhookClient/Pages/Index.cshtml @@ -7,21 +7,25 @@

Welcome

eShopOnContainers - Webhook client

- Login -

Why I need to login? You only need to login to setup a new webhook.

+ @if (!User.Identity.IsAuthenticated) + { + Login +

Why I need to login? You only need to login to setup a new webhook.

+ }
@if (User.Identity.IsAuthenticated) { -
-

Current webhooks invoked

+

Current webhooks received

+

(Data since last time web started up)

@foreach (var webhook in Model.WebHooksReceived) { + }
@webhook.When
@webhook.Data
@(webhook.Token ?? "--None--")
diff --git a/src/Web/WebhookClient/Pages/Index.cshtml.cs b/src/Web/WebhookClient/Pages/Index.cshtml.cs index f38857bc4..08520dd64 100644 --- a/src/Web/WebhookClient/Pages/Index.cshtml.cs +++ b/src/Web/WebhookClient/Pages/Index.cshtml.cs @@ -17,7 +17,7 @@ namespace WebhookClient.Pages public void OnGet() { - WebHooksReceived = HttpContext.Session.Get>("webhooks.received") ?? Enumerable.Empty(); + WebHooksReceived = HttpContext.Session.Get>(SessionKeys.HooksKey) ?? Enumerable.Empty(); } } } diff --git a/src/Web/WebhookClient/Pages/RegisterWebhook.cshtml b/src/Web/WebhookClient/Pages/RegisterWebhook.cshtml index 6c1cff7fb..4eee00b8c 100644 --- a/src/Web/WebhookClient/Pages/RegisterWebhook.cshtml +++ b/src/Web/WebhookClient/Pages/RegisterWebhook.cshtml @@ -11,4 +11,9 @@

Token:

-
\ No newline at end of file + + +@if (Model.ResponseCode != (int)System.Net.HttpStatusCode.OK) +{ +

Error @Model.ResponseCode (@Model.ResponseMessage) when calling the Webhooks API (@Model.RequestUrl) with GrantUrl: @Model.GrantUrl):(

+} \ No newline at end of file diff --git a/src/Web/WebhookClient/Pages/RegisterWebhook.cshtml.cs b/src/Web/WebhookClient/Pages/RegisterWebhook.cshtml.cs index 81ebd38b9..594ba8dfd 100644 --- a/src/Web/WebhookClient/Pages/RegisterWebhook.cshtml.cs +++ b/src/Web/WebhookClient/Pages/RegisterWebhook.cshtml.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Net.Http; using System.Net.Http.Formatting; using System.Threading.Tasks; @@ -20,6 +21,11 @@ namespace WebhookClient.Pages [BindProperty] public string Token { get; set; } + public int ResponseCode { get; set; } + public string RequestUrl { get; set; } + public string GrantUrl { get; set; } + public string ResponseMessage { get; set; } + public RegisterWebhookModel(IOptions settings, IHttpClientFactory httpClientFactory) { @@ -29,15 +35,21 @@ namespace WebhookClient.Pages public void OnGet() { + ResponseCode = (int)HttpStatusCode.OK; Token = _settings.Token; } - public async Task OnPost() + public async Task OnPost() { + ResponseCode = (int)HttpStatusCode.OK; var protocol = Request.IsHttps ? "https" : "http"; var selfurl = !string.IsNullOrEmpty(_settings.SelfUrl) ? _settings.SelfUrl : $"{protocol}://{Request.Host}/{Request.PathBase}"; + if (!selfurl.EndsWith("/")) + { + selfurl = selfurl + "/"; + } var granturl = $"{selfurl}check"; - var url = $"{selfurl}webhook"; + var url = $"{selfurl}webhook-received"; var client = _httpClientFactory.CreateClient("GrantClient"); var payload = new WebhookSubscriptionRequest() @@ -49,7 +61,19 @@ namespace WebhookClient.Pages }; var response = await client.PostAsync(_settings.WebhooksUrl + "/api/v1/webhooks", payload, new JsonMediaTypeFormatter()); - RedirectToPage("Index"); + if (response.IsSuccessStatusCode) + { + return RedirectToPage("WebhooksList"); + } + else + { + ResponseCode = (int)response.StatusCode; + ResponseMessage = response.ReasonPhrase; + GrantUrl = granturl; + RequestUrl = $"{response.RequestMessage.Method} {response.RequestMessage.RequestUri}"; + } + + return Page(); } } } \ No newline at end of file diff --git a/src/Web/WebhookClient/Pages/Shared/_Layout.cshtml b/src/Web/WebhookClient/Pages/Shared/_Layout.cshtml index 053e838a2..e7269f656 100644 --- a/src/Web/WebhookClient/Pages/Shared/_Layout.cshtml +++ b/src/Web/WebhookClient/Pages/Shared/_Layout.cshtml @@ -32,7 +32,10 @@ Home +
@@ -47,7 +50,7 @@ diff --git a/src/Web/WebhookClient/Services/WebhooksClient.cs b/src/Web/WebhookClient/Services/WebhooksClient.cs index 1e4f42414..379c003ad 100644 --- a/src/Web/WebhookClient/Services/WebhooksClient.cs +++ b/src/Web/WebhookClient/Services/WebhooksClient.cs @@ -1,5 +1,8 @@ -using System; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; +using System; using System.Collections.Generic; +using System.Net.Http; using System.Threading.Tasks; using WebhookClient.Models; @@ -7,23 +10,21 @@ namespace WebhookClient.Services { public class WebhooksClient : IWebhooksClient { + + private readonly IHttpClientFactory _httpClientFactory; + private readonly Settings _settings; + public WebhooksClient(IHttpClientFactory httpClientFactory, IOptions settings) + { + _httpClientFactory = httpClientFactory; + _settings = settings.Value; + } public async Task> LoadWebhooks() { - return new[]{ - new WebhookResponse() - { - Date = DateTime.Now, - DestUrl = "http://aaaaa.me", - Token = "3282832as2" - }, - new WebhookResponse() - { - Date = DateTime.Now.Subtract(TimeSpan.FromSeconds(392)), - DestUrl = "http://bbbbb.me", - Token = "ds2" - } - }; - + var client = _httpClientFactory.CreateClient("GrantClient"); + var response = await client.GetAsync(_settings.WebhooksUrl + "/api/v1/webhooks"); + var json = await response.Content.ReadAsStringAsync(); + var subscriptions = JsonConvert.DeserializeObject>(json); + return subscriptions; } } } diff --git a/src/Web/WebhookClient/SessionKeys.cs b/src/Web/WebhookClient/SessionKeys.cs new file mode 100644 index 000000000..741eb5219 --- /dev/null +++ b/src/Web/WebhookClient/SessionKeys.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace WebhookClient +{ + static class SessionKeys + { + public const string HooksKey = "webhooks.received"; + } +} diff --git a/src/Web/WebhookClient/Settings.cs b/src/Web/WebhookClient/Settings.cs index 9a2f9619b..213d272e4 100644 --- a/src/Web/WebhookClient/Settings.cs +++ b/src/Web/WebhookClient/Settings.cs @@ -13,5 +13,7 @@ namespace WebhookClient public string WebhooksUrl { get; set; } public string SelfUrl { get; set; } + public bool ValidateToken { get; set; } + } } diff --git a/src/Web/WebhookClient/Startup.cs b/src/Web/WebhookClient/Startup.cs index b7fa19c34..886e1eac0 100644 --- a/src/Web/WebhookClient/Startup.cs +++ b/src/Web/WebhookClient/Startup.cs @@ -59,9 +59,10 @@ namespace WebhookClient { if ("OPTIONS".Equals(context.Request.Method, StringComparison.InvariantCultureIgnoreCase)) { + var validateToken = bool.TrueString.Equals(Configuration["ValidateToken"], StringComparison.InvariantCultureIgnoreCase); var header = context.Request.Headers[HeaderNames.WebHookCheckHeader]; var value = header.FirstOrDefault(); - if (value == Configuration["Token"]) + if (!validateToken || value == Configuration["Token"]) { context.Response.StatusCode = (int)HttpStatusCode.OK; }