1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-01 08:02:49 -05:00

[EC-307] Fresh desk custom fields integration (#2114)

* Using correct ILogger on FreshdeskController

* Submitting custom fields to Freshdesk

* Set up FreshdeskController to use IHttpClientFactory

* Added unit test for FreshdeskController

* Moved ControllerCustomizeAttribute and ControllerCustomization to Common

* Modified FreshdeskController to use FreshdeskWebhookModel; Edited unit tests to use AutoFixture
This commit is contained in:
Rui Tomé
2022-07-15 17:10:56 +01:00
committed by GitHub
parent 448e255fb6
commit 6e19bfeb22
10 changed files with 140 additions and 42 deletions

View File

@ -1,7 +1,7 @@
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using System.Text;
using System.Text.Json;
using Bit.Billing.Models;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Bit.Core.Utilities;
@ -17,18 +17,18 @@ namespace Bit.Billing.Controllers
private readonly IUserRepository _userRepository;
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly ILogger<AppleController> _logger;
private readonly ILogger<FreshdeskController> _logger;
private readonly GlobalSettings _globalSettings;
private readonly HttpClient _httpClient = new HttpClient();
private readonly string _freshdeskAuthkey;
private readonly IHttpClientFactory _httpClientFactory;
public FreshdeskController(
IUserRepository userRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IOptions<BillingSettings> billingSettings,
ILogger<AppleController> logger,
GlobalSettings globalSettings)
ILogger<FreshdeskController> logger,
GlobalSettings globalSettings,
IHttpClientFactory httpClientFactory)
{
_billingSettings = billingSettings?.Value;
_userRepository = userRepository;
@ -36,37 +36,23 @@ namespace Bit.Billing.Controllers
_organizationUserRepository = organizationUserRepository;
_logger = logger;
_globalSettings = globalSettings;
_freshdeskAuthkey = Convert.ToBase64String(
Encoding.UTF8.GetBytes($"{_billingSettings.FreshdeskApiKey}:X"));
_httpClientFactory = httpClientFactory;
}
[HttpPost("webhook")]
public async Task<IActionResult> PostWebhook()
public async Task<IActionResult> PostWebhook([FromQuery, Required] string key,
[FromBody, Required] FreshdeskWebhookModel model)
{
if (HttpContext?.Request?.Query == null)
{
return new BadRequestResult();
}
var key = HttpContext.Request.Query.ContainsKey("key") ?
HttpContext.Request.Query["key"].ToString() : null;
if (!CoreHelpers.FixedTimeEquals(key, _billingSettings.FreshdeskWebhookKey))
{
return new BadRequestResult();
}
using var body = await JsonSerializer.DeserializeAsync<JsonDocument>(HttpContext.Request.Body);
var root = body.RootElement;
if (root.ValueKind != JsonValueKind.Object)
if (string.IsNullOrWhiteSpace(key) || !CoreHelpers.FixedTimeEquals(key, _billingSettings.FreshdeskWebhookKey))
{
return new BadRequestResult();
}
try
{
var ticketId = root.GetProperty("ticket_id").GetString();
var ticketContactEmail = root.GetProperty("ticket_contact_email").GetString();
var ticketTags = root.GetProperty("ticket_tags").GetString();
var ticketId = model.TicketId;
var ticketContactEmail = model.TicketContactEmail;
var ticketTags = model.TicketTags;
if (string.IsNullOrWhiteSpace(ticketId) || string.IsNullOrWhiteSpace(ticketContactEmail))
{
return new BadRequestResult();
@ -74,20 +60,34 @@ namespace Bit.Billing.Controllers
var updateBody = new Dictionary<string, object>();
var note = string.Empty;
var customFields = new Dictionary<string, object>();
var user = await _userRepository.GetByEmailAsync(ticketContactEmail);
if (user != null)
{
note += $"<li>User, {user.Email}: {_globalSettings.BaseServiceUri.Admin}/users/edit/{user.Id}</li>";
var userLink = $"{_globalSettings.BaseServiceUri.Admin}/users/edit/{user.Id}";
note += $"<li>User, {user.Email}: {userLink}</li>";
customFields.Add("cf_user", userLink);
var tags = new HashSet<string>();
if (user.Premium)
{
tags.Add("Premium");
}
var orgs = await _organizationRepository.GetManyByUserIdAsync(user.Id);
foreach (var org in orgs)
{
note += $"<li>Org, {org.Name} ({org.Seats.GetValueOrDefault()}): " +
$"{_globalSettings.BaseServiceUri.Admin}/organizations/edit/{org.Id}</li>";
var orgNote = $"{org.Name} ({org.Seats.GetValueOrDefault()}): " +
$"{_globalSettings.BaseServiceUri.Admin}/organizations/edit/{org.Id}";
note += $"<li>Org, {orgNote}</li>";
if (!customFields.Any(kvp => kvp.Key == "cf_org"))
{
customFields.Add("cf_org", orgNote);
}
else
{
customFields["cf_org"] += $"\n{orgNote}";
}
var planName = GetAttribute<DisplayAttribute>(org.PlanType).Name.Split(" ").FirstOrDefault();
if (!string.IsNullOrWhiteSpace(planName))
{
@ -107,15 +107,18 @@ namespace Bit.Billing.Controllers
}
updateBody.Add("tags", tagsToUpdate);
}
if (customFields.Any())
{
updateBody.Add("custom_fields", customFields);
}
var updateRequest = new HttpRequestMessage(HttpMethod.Put,
string.Format("https://bitwarden.freshdesk.com/api/v2/tickets/{0}", ticketId))
{
Content = JsonContent.Create(updateBody),
};
await CallFreshdeskApiAsync(updateRequest);
var noteBody = new Dictionary<string, object>
{
{ "body", $"<ul>{note}</ul>" },
@ -142,8 +145,10 @@ namespace Bit.Billing.Controllers
{
try
{
request.Headers.Add("Authorization", _freshdeskAuthkey);
var response = await _httpClient.SendAsync(request);
var freshdeskAuthkey = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{_billingSettings.FreshdeskApiKey}:X"));
var httpClient = _httpClientFactory.CreateClient("FreshdeskApi");
request.Headers.Add("Authorization", freshdeskAuthkey);
var response = await httpClient.SendAsync(request);
if (response.StatusCode != System.Net.HttpStatusCode.TooManyRequests || retriedCount > 3)
{
return response;

View File

@ -0,0 +1,16 @@
using System.Text.Json.Serialization;
namespace Bit.Billing.Models
{
public class FreshdeskWebhookModel
{
[JsonPropertyName("ticket_id")]
public string TicketId { get; set; }
[JsonPropertyName("ticket_contact_email")]
public string TicketContactEmail { get; set; }
[JsonPropertyName("ticket_tags")]
public string TicketTags { get; set; }
}
}

View File

@ -68,6 +68,9 @@ namespace Bit.Billing
// Jobs service, uncomment when we have some jobs to run
// Jobs.JobsHostedService.AddJobsServices(services);
// services.AddHostedService<Jobs.JobsHostedService>();
// Set up HttpClients
services.AddHttpClient("FreshdeskApi");
}
public void Configure(