mirror of
https://github.com/bitwarden/server.git
synced 2025-04-20 04:28:13 -05:00
[AC-1637] Sanitize Business and Organization Names from html script injection prior to storing in db (#3302)
* [AC-1637] Added HtmlEncodingStringConverter to encode/decode special chars on JSON serialization/deserialization * [AC-1637] Added unit tests for HtmlEncodingStringConverter * [AC-1637] Moved expected values on unit tests to the arrange phase * [AC-1637] Added HtmlEncodingStringConverter to properties that are for input/output of Org Name and Business name * [AC-1637] Modified views in Admin project to decode values to display * [AC-1637] Replaced Html.Raw with HttpUtility.HtmlDecode * [AC-1637] Added JsonConverter to Provider DTOs * [AC-1637] Modified HandlebarsMailService to decode organization name before sending emails * Revert "[AC-1637] Added JsonConverter to Provider DTOs" This reverts commit 94d507cf93e4c9f7f02890b9286dba90bad3f516. * [AC-1637] Fixed Admin panel organization search * [AC-1637] Sanitizing Organization name and business name on creation in Admin panel * [AC-1637] Sanitizing organization name and business name on creation by a provider * [AC-1637] Sanitizing provider name on creation and on viewing in admin panel * [AC-1637] Added sanitization to more places where Org name is used * [AC-1637] Swapped using HttpUtility for WebUtility since the later is part of the dotnet framework * [AC-1637] Updated error messages * [AC-1637] Decoding on Admin panel add existing organization * [AC-1637] Fix HTML decoding issues * [AC-1637] Refactor HTML decoding in View and Model classes on Admin panel * [AC-1637] Refactor provider name and business name usages to use methods that output decoded values * [AC-1637] Fixed typo * [AC-1637] Renamed Provider methods to retrieve Decoded Name and BusinessName * [AC-1637] Renamed Organization methods to retrieve Decoded Name and BusinessName * [AC-1637] Update the display name method in the `ProviderOrganizationOrganizationDetails` class to `DisplayName()`
This commit is contained in:
parent
997af0f6ab
commit
9d59e4dc9e
@ -257,7 +257,7 @@ public class ProviderService : IProviderService
|
|||||||
|
|
||||||
await _providerUserRepository.ReplaceAsync(providerUser);
|
await _providerUserRepository.ReplaceAsync(providerUser);
|
||||||
events.Add((providerUser, EventType.ProviderUser_Confirmed, null));
|
events.Add((providerUser, EventType.ProviderUser_Confirmed, null));
|
||||||
await _mailService.SendProviderConfirmedEmailAsync(provider.Name, user.Email);
|
await _mailService.SendProviderConfirmedEmailAsync(provider.DisplayName(), user.Email);
|
||||||
result.Add(Tuple.Create(providerUser, ""));
|
result.Add(Tuple.Create(providerUser, ""));
|
||||||
}
|
}
|
||||||
catch (BadRequestException e)
|
catch (BadRequestException e)
|
||||||
@ -331,7 +331,7 @@ public class ProviderService : IProviderService
|
|||||||
var email = user == null ? providerUser.Email : user.Email;
|
var email = user == null ? providerUser.Email : user.Email;
|
||||||
if (!string.IsNullOrWhiteSpace(email))
|
if (!string.IsNullOrWhiteSpace(email))
|
||||||
{
|
{
|
||||||
await _mailService.SendProviderUserRemoved(provider.Name, email);
|
await _mailService.SendProviderUserRemoved(provider.DisplayName(), email);
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Add(Tuple.Create(providerUser, ""));
|
result.Add(Tuple.Create(providerUser, ""));
|
||||||
@ -586,7 +586,7 @@ public class ProviderService : IProviderService
|
|||||||
var nowMillis = CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow);
|
var nowMillis = CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow);
|
||||||
var token = _dataProtector.Protect(
|
var token = _dataProtector.Protect(
|
||||||
$"ProviderUserInvite {providerUser.Id} {providerUser.Email} {nowMillis}");
|
$"ProviderUserInvite {providerUser.Id} {providerUser.Email} {nowMillis}");
|
||||||
await _mailService.SendProviderInviteEmailAsync(provider.Name, providerUser, token, providerUser.Email);
|
await _mailService.SendProviderInviteEmailAsync(provider.DisplayName(), providerUser, token, providerUser.Email);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> HasConfirmedProviderAdminExceptAsync(Guid providerId, IEnumerable<Guid> providerUserIds)
|
private async Task<bool> HasConfirmedProviderAdminExceptAsync(Guid providerId, IEnumerable<Guid> providerUserIds)
|
||||||
|
@ -26,6 +26,7 @@ using IdentityModel;
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using AuthenticationSchemes = Bit.Core.AuthenticationSchemes;
|
||||||
using DIM = Duende.IdentityServer.Models;
|
using DIM = Duende.IdentityServer.Models;
|
||||||
|
|
||||||
namespace Bit.Sso.Controllers;
|
namespace Bit.Sso.Controllers;
|
||||||
@ -483,7 +484,7 @@ public class AccountController : Controller
|
|||||||
if (orgUser.Status == OrganizationUserStatusType.Invited)
|
if (orgUser.Status == OrganizationUserStatusType.Invited)
|
||||||
{
|
{
|
||||||
// Org User is invited - they must manually accept the invite via email and authenticate with MP
|
// Org User is invited - they must manually accept the invite via email and authenticate with MP
|
||||||
throw new Exception(_i18nService.T("UserAlreadyInvited", email, organization.Name));
|
throw new Exception(_i18nService.T("UserAlreadyInvited", email, organization.DisplayName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accepted or Confirmed - create SSO link and return;
|
// Accepted or Confirmed - create SSO link and return;
|
||||||
@ -516,7 +517,7 @@ public class AccountController : Controller
|
|||||||
await _organizationService.AdjustSeatsAsync(orgId, initialSeatCount - organization.Seats.Value, prorationDate);
|
await _organizationService.AdjustSeatsAsync(orgId, initialSeatCount - organization.Seats.Value, prorationDate);
|
||||||
}
|
}
|
||||||
_logger.LogInformation(e, "SSO auto provisioning failed");
|
_logger.LogInformation(e, "SSO auto provisioning failed");
|
||||||
throw new Exception(_i18nService.T("NoSeatsAvailable", organization.Name));
|
throw new Exception(_i18nService.T("NoSeatsAvailable", organization.DisplayName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Admin.AdminConsole.Models;
|
using System.Net;
|
||||||
|
using Bit.Admin.AdminConsole.Models;
|
||||||
using Bit.Admin.Enums;
|
using Bit.Admin.Enums;
|
||||||
using Bit.Admin.Services;
|
using Bit.Admin.Services;
|
||||||
using Bit.Admin.Utilities;
|
using Bit.Admin.Utilities;
|
||||||
@ -119,8 +120,9 @@ public class OrganizationsController : Controller
|
|||||||
count = 1;
|
count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var encodedName = WebUtility.HtmlEncode(name);
|
||||||
var skip = (page - 1) * count;
|
var skip = (page - 1) * count;
|
||||||
var organizations = await _organizationRepository.SearchAsync(name, userEmail, paid, skip, count);
|
var organizations = await _organizationRepository.SearchAsync(encodedName, userEmail, paid, skip, count);
|
||||||
return View(new OrganizationsModel
|
return View(new OrganizationsModel
|
||||||
{
|
{
|
||||||
Items = organizations as List<Organization>,
|
Items = organizations as List<Organization>,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Admin.AdminConsole.Models;
|
using System.Net;
|
||||||
|
using Bit.Admin.AdminConsole.Models;
|
||||||
using Bit.Admin.Enums;
|
using Bit.Admin.Enums;
|
||||||
using Bit.Admin.Utilities;
|
using Bit.Admin.Utilities;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
@ -188,8 +189,9 @@ public class ProvidersController : Controller
|
|||||||
count = 1;
|
count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var encodedName = WebUtility.HtmlEncode(name);
|
||||||
var skip = (page - 1) * count;
|
var skip = (page - 1) * count;
|
||||||
var unassignedOrganizations = await _organizationRepository.SearchUnassignedToProviderAsync(name, ownerEmail, skip, count);
|
var unassignedOrganizations = await _organizationRepository.SearchUnassignedToProviderAsync(encodedName, ownerEmail, skip, count);
|
||||||
var viewModel = new OrganizationUnassignedToProviderSearchViewModel
|
var viewModel = new OrganizationUnassignedToProviderSearchViewModel
|
||||||
{
|
{
|
||||||
OrganizationName = string.IsNullOrWhiteSpace(name) ? null : name,
|
OrganizationName = string.IsNullOrWhiteSpace(name) ? null : name,
|
||||||
@ -199,7 +201,7 @@ public class ProvidersController : Controller
|
|||||||
Items = unassignedOrganizations.Select(uo => new OrganizationSelectableViewModel
|
Items = unassignedOrganizations.Select(uo => new OrganizationSelectableViewModel
|
||||||
{
|
{
|
||||||
Id = uo.Id,
|
Id = uo.Id,
|
||||||
Name = uo.Name,
|
Name = uo.DisplayName(),
|
||||||
PlanType = uo.PlanType
|
PlanType = uo.PlanType
|
||||||
}).ToList()
|
}).ToList()
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Net;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
@ -36,8 +37,8 @@ public class OrganizationEditModel : OrganizationViewModel
|
|||||||
BillingInfo = billingInfo;
|
BillingInfo = billingInfo;
|
||||||
BraintreeMerchantId = globalSettings.Braintree.MerchantId;
|
BraintreeMerchantId = globalSettings.Braintree.MerchantId;
|
||||||
|
|
||||||
Name = org.Name;
|
Name = org.DisplayName();
|
||||||
BusinessName = org.BusinessName;
|
BusinessName = org.DisplayBusinessName();
|
||||||
BillingEmail = provider?.Type == ProviderType.Reseller ? provider.BillingEmail : org.BillingEmail;
|
BillingEmail = provider?.Type == ProviderType.Reseller ? provider.BillingEmail : org.BillingEmail;
|
||||||
PlanType = org.PlanType;
|
PlanType = org.PlanType;
|
||||||
Plan = org.Plan;
|
Plan = org.Plan;
|
||||||
@ -184,8 +185,8 @@ public class OrganizationEditModel : OrganizationViewModel
|
|||||||
|
|
||||||
public Organization ToOrganization(Organization existingOrganization)
|
public Organization ToOrganization(Organization existingOrganization)
|
||||||
{
|
{
|
||||||
existingOrganization.Name = Name;
|
existingOrganization.Name = WebUtility.HtmlEncode(Name.Trim());
|
||||||
existingOrganization.BusinessName = BusinessName;
|
existingOrganization.BusinessName = WebUtility.HtmlEncode(BusinessName.Trim());
|
||||||
existingOrganization.BillingEmail = BillingEmail?.ToLowerInvariant()?.Trim();
|
existingOrganization.BillingEmail = BillingEmail?.ToLowerInvariant()?.Trim();
|
||||||
existingOrganization.PlanType = PlanType.Value;
|
existingOrganization.PlanType = PlanType.Value;
|
||||||
existingOrganization.Plan = Plan;
|
existingOrganization.Plan = Plan;
|
||||||
|
@ -11,8 +11,8 @@ public class ProviderEditModel : ProviderViewModel
|
|||||||
public ProviderEditModel(Provider provider, IEnumerable<ProviderUserUserDetails> providerUsers, IEnumerable<ProviderOrganizationOrganizationDetails> organizations)
|
public ProviderEditModel(Provider provider, IEnumerable<ProviderUserUserDetails> providerUsers, IEnumerable<ProviderOrganizationOrganizationDetails> organizations)
|
||||||
: base(provider, providerUsers, organizations)
|
: base(provider, providerUsers, organizations)
|
||||||
{
|
{
|
||||||
Name = provider.Name;
|
Name = provider.DisplayName();
|
||||||
BusinessName = provider.BusinessName;
|
BusinessName = provider.DisplayBusinessName();
|
||||||
BillingEmail = provider.BillingEmail;
|
BillingEmail = provider.BillingEmail;
|
||||||
BillingPhone = provider.BillingPhone;
|
BillingPhone = provider.BillingPhone;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
@inject Bit.Admin.Services.IAccessControlService AccessControlService
|
@inject Bit.Admin.Services.IAccessControlService AccessControlService
|
||||||
@model OrganizationEditModel
|
@model OrganizationEditModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = (Model.Provider != null ? "Client " : string.Empty) + "Organization: " + Model.Organization.Name;
|
ViewData["Title"] = (Model.Provider != null ? "Client " : string.Empty) + "Organization: " + Model.Name;
|
||||||
|
|
||||||
var canViewOrganizationInformation = AccessControlService.UserHasPermission(Permission.Org_OrgInformation_View);
|
var canViewOrganizationInformation = AccessControlService.UserHasPermission(Permission.Org_OrgInformation_View);
|
||||||
var canViewBillingInformation = AccessControlService.UserHasPermission(Permission.Org_BillingInformation_View);
|
var canViewBillingInformation = AccessControlService.UserHasPermission(Permission.Org_BillingInformation_View);
|
||||||
@ -58,7 +58,7 @@
|
|||||||
</script>
|
</script>
|
||||||
}
|
}
|
||||||
|
|
||||||
<h1>@(Model.Provider != null ? "Client " : string.Empty)Organization <small>@Model.Organization.Name</small></h1>
|
<h1>@(Model.Provider != null ? "Client " : string.Empty)Organization <small>@Model.Name</small></h1>
|
||||||
|
|
||||||
@if (Model.Provider != null)
|
@if (Model.Provider != null)
|
||||||
{
|
{
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a asp-action="@Model.Action" asp-route-id="@org.Id">@org.Name</a>
|
<a asp-action="@Model.Action" asp-route-id="@org.Id">@org.DisplayName()</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@org.Plan
|
@org.Plan
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
@inject Bit.Core.Settings.GlobalSettings GlobalSettings
|
@inject Bit.Core.Settings.GlobalSettings GlobalSettings
|
||||||
@model OrganizationViewModel
|
@model OrganizationViewModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Organization: " + Model.Organization.Name;
|
ViewData["Title"] = "Organization: " + Model.Organization.DisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
<h1>Organization <small>@Model.Organization.Name</small></h1>
|
<h1>Organization <small>@Model.Organization.DisplayName()</small></h1>
|
||||||
|
|
||||||
@if (Model.Provider != null)
|
@if (Model.Provider != null)
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
@model Bit.Core.AdminConsole.Entities.Provider.Provider
|
@model Bit.Core.AdminConsole.Entities.Provider.Provider
|
||||||
<dl class="row">
|
<dl class="row">
|
||||||
<dt class="col-sm-4 col-lg-3">Provider Name</dt>
|
<dt class="col-sm-4 col-lg-3">Provider Name</dt>
|
||||||
<dd class="col-sm-8 col-lg-9">@Model.Name</dd>
|
<dd class="col-sm-8 col-lg-9">@Model.DisplayName()</dd>
|
||||||
|
|
||||||
<dt class="col-sm-4 col-lg-3">Provider Type</dt>
|
<dt class="col-sm-4 col-lg-3">Provider Type</dt>
|
||||||
<dd class="col-sm-8 col-lg-9">@(Model.Type.GetDisplayAttribute()?.GetName())</dd>
|
<dd class="col-sm-8 col-lg-9">@(Model.Type.GetDisplayAttribute()?.GetName())</dd>
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
@Html.HiddenFor(m => Model.Items[i].Id, new { @readonly = "readonly", autocomplete = "off" })
|
@Html.HiddenFor(m => Model.Items[i].Id, new { @readonly = "readonly", autocomplete = "off" })
|
||||||
@Html.CheckBoxFor(m => Model.Items[i].Selected)
|
@Html.CheckBoxFor(m => Model.Items[i].Selected)
|
||||||
</td>
|
</td>
|
||||||
<td>@Html.ActionLink(Model.Items[i].Name, "Edit", "Organizations", new { id = Model.Items[i].Id }, new { target = "_blank" })</td>
|
<td>@Html.ActionLink(Model.Items[i].DisplayName(), "Edit", "Organizations", new { id = Model.Items[i].Id }, new { target = "_blank" })</td>
|
||||||
<td>@(Model.Items[i].PlanType.GetDisplayAttribute()?.Name ?? Model.Items[i].PlanType.ToString())</td>
|
<td>@(Model.Items[i].PlanType.GetDisplayAttribute()?.Name ?? Model.Items[i].PlanType.ToString())</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
|
|
||||||
@model ProviderEditModel
|
@model ProviderEditModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Provider: " + Model.Provider.Name;
|
ViewData["Title"] = "Provider: " + Model.Provider.DisplayName();
|
||||||
|
|
||||||
var canEdit = AccessControlService.UserHasPermission(Permission.Provider_Edit);
|
var canEdit = AccessControlService.UserHasPermission(Permission.Provider_Edit);
|
||||||
}
|
}
|
||||||
|
|
||||||
<h1>Provider <small>@Model.Provider.Name</small></h1>
|
<h1>Provider <small>@Model.Provider.DisplayName()</small></h1>
|
||||||
|
|
||||||
<h2>Provider Information</h2>
|
<h2>Provider Information</h2>
|
||||||
@await Html.PartialAsync("_ViewInformation", Model)
|
@await Html.PartialAsync("_ViewInformation", Model)
|
||||||
@ -17,12 +17,12 @@
|
|||||||
<h2>General</h2>
|
<h2>General</h2>
|
||||||
<dl class="row">
|
<dl class="row">
|
||||||
<dt class="col-sm-4 col-lg-3">Name</dt>
|
<dt class="col-sm-4 col-lg-3">Name</dt>
|
||||||
<dd class="col-sm-8 col-lg-9">@Model.Provider.Name</dd>
|
<dd class="col-sm-8 col-lg-9">@Model.Provider.DisplayName()</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<h2>Business Information</h2>
|
<h2>Business Information</h2>
|
||||||
<dl class="row">
|
<dl class="row">
|
||||||
<dt class="col-sm-4 col-lg-3">Business Name</dt>
|
<dt class="col-sm-4 col-lg-3">Business Name</dt>
|
||||||
<dd class="col-sm-8 col-lg-9">@Model.Provider.BusinessName</dd>
|
<dd class="col-sm-8 col-lg-9">@Model.Provider.DisplayBusinessName()</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<h2>Billing</h2>
|
<h2>Billing</h2>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a asp-action="@Model.Action" asp-route-id="@provider.Id">@(provider.Name ?? "Pending")</a>
|
<a asp-action="@Model.Action" asp-route-id="@provider.Id">@(!string.IsNullOrEmpty(provider.DisplayName()) ? provider.DisplayName() : "Pending")</a>
|
||||||
</td>
|
</td>
|
||||||
<td>@provider.Type.GetDisplayAttribute()?.GetShortName()</td>
|
<td>@provider.Type.GetDisplayAttribute()?.GetShortName()</td>
|
||||||
<td>@provider.Status</td>
|
<td>@provider.Status</td>
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
<a asp-controller="Organizations" asp-action="Edit" asp-route-id="@providerOrganization.OrganizationId">@providerOrganization.OrganizationName</a>
|
<a asp-controller="Organizations" asp-action="Edit" asp-route-id="@providerOrganization.OrganizationId">@providerOrganization.DisplayName()</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@providerOrganization.Status
|
@providerOrganization.Status
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
@model ProviderViewModel
|
@model ProviderViewModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Provider: " + Model.Provider.Name;
|
ViewData["Title"] = "Provider: " + Model.Provider.DisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
<h1>Provider <small>@Model.Provider.Name</small></h1>
|
<h1>Provider <small>@Model.Provider.DisplayName()</small></h1>
|
||||||
|
|
||||||
<h2>Information</h2>
|
<h2>Information</h2>
|
||||||
@await Html.PartialAsync("_ViewInformation", Model)
|
@await Html.PartialAsync("_ViewInformation", Model)
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="Name"></label>
|
<label asp-for="Name"></label>
|
||||||
<input type="text" class="form-control" asp-for="Name" required>
|
<input type="text" class="form-control" asp-for="Name" value="@Model.Name" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -68,7 +68,7 @@
|
|||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="BusinessName"></label>
|
<label asp-for="BusinessName"></label>
|
||||||
<input type="text" class="form-control" asp-for="BusinessName">
|
<input type="text" class="form-control" asp-for="BusinessName" value="@Model.BusinessName">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -303,7 +303,7 @@ public class OrganizationsController : Controller
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var updateBilling = !_globalSettings.SelfHosted && (model.BusinessName != organization.BusinessName ||
|
var updateBilling = !_globalSettings.SelfHosted && (model.BusinessName != organization.DisplayBusinessName() ||
|
||||||
model.BillingEmail != organization.BillingEmail);
|
model.BillingEmail != organization.BillingEmail);
|
||||||
|
|
||||||
var hasRequiredPermissions = updateBilling
|
var hasRequiredPermissions = updateBilling
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
@ -9,9 +10,11 @@ namespace Bit.Api.AdminConsole.Models.Request.Organizations;
|
|||||||
public class OrganizationCreateRequestModel : IValidatableObject
|
public class OrganizationCreateRequestModel : IValidatableObject
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
[StringLength(50)]
|
[StringLength(50, ErrorMessage = "The field Name exceeds the maximum length.")]
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
[StringLength(50)]
|
[StringLength(50, ErrorMessage = "The field Business Name exceeds the maximum length.")]
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string BusinessName { get; set; }
|
public string BusinessName { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
[StringLength(256)]
|
[StringLength(256)]
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Api.AdminConsole.Models.Request.Organizations;
|
namespace Bit.Api.AdminConsole.Models.Request.Organizations;
|
||||||
|
|
||||||
public class OrganizationUpdateRequestModel
|
public class OrganizationUpdateRequestModel
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
[StringLength(50)]
|
[StringLength(50, ErrorMessage = "The field Name exceeds the maximum length.")]
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
[StringLength(50)]
|
[StringLength(50, ErrorMessage = "The field Business Name exceeds the maximum length.")]
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string BusinessName { get; set; }
|
public string BusinessName { get; set; }
|
||||||
[EmailAddress]
|
[EmailAddress]
|
||||||
[Required]
|
[Required]
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Api.AdminConsole.Models.Request.Providers;
|
namespace Bit.Api.AdminConsole.Models.Request.Providers;
|
||||||
|
|
||||||
public class ProviderSetupRequestModel
|
public class ProviderSetupRequestModel
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
[StringLength(50)]
|
[StringLength(50, ErrorMessage = "The field Name exceeds the maximum length.")]
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
[StringLength(50)]
|
[StringLength(50, ErrorMessage = "The field Business Name exceeds the maximum length.")]
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string BusinessName { get; set; }
|
public string BusinessName { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
[StringLength(256)]
|
[StringLength(256)]
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Api.AdminConsole.Models.Request.Providers;
|
namespace Bit.Api.AdminConsole.Models.Request.Providers;
|
||||||
|
|
||||||
public class ProviderUpdateRequestModel
|
public class ProviderUpdateRequestModel
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
[StringLength(50)]
|
[StringLength(50, ErrorMessage = "The field Name exceeds the maximum length.")]
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
[StringLength(50)]
|
[StringLength(50, ErrorMessage = "The field Business Name exceeds the maximum length.")]
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string BusinessName { get; set; }
|
public string BusinessName { get; set; }
|
||||||
[EmailAddress]
|
[EmailAddress]
|
||||||
[Required]
|
[Required]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Api.Models.Response;
|
using System.Text.Json.Serialization;
|
||||||
|
using Bit.Api.Models.Response;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Api;
|
using Bit.Core.Models.Api;
|
||||||
@ -60,7 +61,9 @@ public class OrganizationResponseModel : ResponseModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string BusinessName { get; set; }
|
public string BusinessName { get; set; }
|
||||||
public string BusinessAddress1 { get; set; }
|
public string BusinessAddress1 { get; set; }
|
||||||
public string BusinessAddress2 { get; set; }
|
public string BusinessAddress2 { get; set; }
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using System.Text.Json.Serialization;
|
||||||
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.Auth.Enums;
|
using Bit.Core.Auth.Enums;
|
||||||
using Bit.Core.Auth.Models.Data;
|
using Bit.Core.Auth.Models.Data;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -103,6 +104,7 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public bool UsePolicies { get; set; }
|
public bool UsePolicies { get; set; }
|
||||||
public bool UseSso { get; set; }
|
public bool UseSso { get; set; }
|
||||||
@ -135,6 +137,7 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
|||||||
public Guid? UserId { get; set; }
|
public Guid? UserId { get; set; }
|
||||||
public bool HasPublicAndPrivateKeys { get; set; }
|
public bool HasPublicAndPrivateKeys { get; set; }
|
||||||
public Guid? ProviderId { get; set; }
|
public Guid? ProviderId { get; set; }
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string ProviderName { get; set; }
|
public string ProviderName { get; set; }
|
||||||
public ProviderType? ProviderType { get; set; }
|
public ProviderType? ProviderType { get; set; }
|
||||||
public string FamilySponsorshipFriendlyName { get; set; }
|
public string FamilySponsorshipFriendlyName { get; set; }
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using System.Text.Json.Serialization;
|
||||||
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Models.Data.Provider;
|
using Bit.Core.AdminConsole.Models.Data.Provider;
|
||||||
using Bit.Core.Models.Api;
|
using Bit.Core.Models.Api;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
@ -23,6 +24,7 @@ public class ProfileProviderResponseModel : ResponseModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
public ProviderUserStatusType Status { get; set; }
|
public ProviderUserStatusType Status { get; set; }
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using System.Text.Json.Serialization;
|
||||||
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Models.Data.Provider;
|
using Bit.Core.AdminConsole.Models.Data.Provider;
|
||||||
using Bit.Core.Models.Api;
|
using Bit.Core.Models.Api;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Api.AdminConsole.Models.Response.Providers;
|
namespace Bit.Api.AdminConsole.Models.Response.Providers;
|
||||||
|
|
||||||
@ -68,5 +70,6 @@ public class ProviderOrganizationOrganizationDetailsResponseModel : ProviderOrga
|
|||||||
OrganizationName = providerOrganization.OrganizationName;
|
OrganizationName = providerOrganization.OrganizationName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string OrganizationName { get; set; }
|
public string OrganizationName { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using System.Text.Json.Serialization;
|
||||||
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.Models.Api;
|
using Bit.Core.Models.Api;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Api.AdminConsole.Models.Response.Providers;
|
namespace Bit.Api.AdminConsole.Models.Response.Providers;
|
||||||
|
|
||||||
@ -25,6 +27,7 @@ public class ProviderResponseModel : ResponseModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string BusinessName { get; set; }
|
public string BusinessName { get; set; }
|
||||||
public string BusinessAddress1 { get; set; }
|
public string BusinessAddress1 { get; set; }
|
||||||
|
@ -132,7 +132,7 @@ public class OrganizationSponsorshipsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (syncResponseData, offersToSend) = await _syncSponsorshipsCommand.SyncOrganization(sponsoringOrg, model.ToOrganizationSponsorshipSync().SponsorshipsBatch);
|
var (syncResponseData, offersToSend) = await _syncSponsorshipsCommand.SyncOrganization(sponsoringOrg, model.ToOrganizationSponsorshipSync().SponsorshipsBatch);
|
||||||
await _sendSponsorshipOfferCommand.BulkSendSponsorshipOfferAsync(sponsoringOrg.Name, offersToSend);
|
await _sendSponsorshipOfferCommand.BulkSendSponsorshipOfferAsync(sponsoringOrg.DisplayName(), offersToSend);
|
||||||
return new OrganizationSponsorshipSyncResponseModel(syncResponseData);
|
return new OrganizationSponsorshipSyncResponseModel(syncResponseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ public class SelfHostedSponsorshipSyncJob : BaseJob
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, $"Sponsorship sync for organization {org.Name} Failed");
|
_logger.LogError(ex, "Sponsorship sync for organization {OrganizationName} Failed", org.DisplayName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ public class FreshsalesController : Controller
|
|||||||
|
|
||||||
foreach (var org in orgs)
|
foreach (var org in orgs)
|
||||||
{
|
{
|
||||||
noteItems.Add($"Org, {org.Name}: {_globalSettings.BaseServiceUri.Admin}/organizations/edit/{org.Id}");
|
noteItems.Add($"Org, {org.DisplayName()}: {_globalSettings.BaseServiceUri.Admin}/organizations/edit/{org.Id}");
|
||||||
if (TryGetPlanName(org.PlanType, out var planName))
|
if (TryGetPlanName(org.PlanType, out var planName))
|
||||||
{
|
{
|
||||||
newTags.Add($"Org: {planName}");
|
newTags.Add($"Org: {planName}");
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Net;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Bit.Core.Auth.Enums;
|
using Bit.Core.Auth.Enums;
|
||||||
using Bit.Core.Auth.Models;
|
using Bit.Core.Auth.Models;
|
||||||
@ -17,8 +18,14 @@ public class Organization : ITableObject<Guid>, IStorableSubscriber, IRevisable,
|
|||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
[MaxLength(50)]
|
[MaxLength(50)]
|
||||||
public string Identifier { get; set; }
|
public string Identifier { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// This value is HTML encoded. For display purposes use the method DisplayName() instead.
|
||||||
|
/// </summary>
|
||||||
[MaxLength(50)]
|
[MaxLength(50)]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// This value is HTML encoded. For display purposes use the method DisplayBusinessName() instead.
|
||||||
|
/// </summary>
|
||||||
[MaxLength(50)]
|
[MaxLength(50)]
|
||||||
public string BusinessName { get; set; }
|
public string BusinessName { get; set; }
|
||||||
[MaxLength(50)]
|
[MaxLength(50)]
|
||||||
@ -104,6 +111,22 @@ public class Organization : ITableObject<Guid>, IStorableSubscriber, IRevisable,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the name of the organization, HTML decoded ready for display.
|
||||||
|
/// </summary>
|
||||||
|
public string DisplayName()
|
||||||
|
{
|
||||||
|
return WebUtility.HtmlDecode(Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the business name of the organization, HTML decoded ready for display.
|
||||||
|
/// </summary>
|
||||||
|
public string DisplayBusinessName()
|
||||||
|
{
|
||||||
|
return WebUtility.HtmlDecode(BusinessName);
|
||||||
|
}
|
||||||
|
|
||||||
public string BillingEmailAddress()
|
public string BillingEmailAddress()
|
||||||
{
|
{
|
||||||
return BillingEmail?.ToLowerInvariant()?.Trim();
|
return BillingEmail?.ToLowerInvariant()?.Trim();
|
||||||
@ -111,12 +134,12 @@ public class Organization : ITableObject<Guid>, IStorableSubscriber, IRevisable,
|
|||||||
|
|
||||||
public string BillingName()
|
public string BillingName()
|
||||||
{
|
{
|
||||||
return BusinessName;
|
return DisplayBusinessName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SubscriberName()
|
public string SubscriberName()
|
||||||
{
|
{
|
||||||
return Name;
|
return DisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string BraintreeCustomerIdPrefix()
|
public string BraintreeCustomerIdPrefix()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using System.Net;
|
||||||
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
@ -7,7 +8,13 @@ namespace Bit.Core.AdminConsole.Entities.Provider;
|
|||||||
public class Provider : ITableObject<Guid>
|
public class Provider : ITableObject<Guid>
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// This value is HTML encoded. For display purposes use the method DisplayName() instead.
|
||||||
|
/// </summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// This value is HTML encoded. For display purposes use the method DisplayBusinessName() instead.
|
||||||
|
/// </summary>
|
||||||
public string BusinessName { get; set; }
|
public string BusinessName { get; set; }
|
||||||
public string BusinessAddress1 { get; set; }
|
public string BusinessAddress1 { get; set; }
|
||||||
public string BusinessAddress2 { get; set; }
|
public string BusinessAddress2 { get; set; }
|
||||||
@ -30,4 +37,20 @@ public class Provider : ITableObject<Guid>
|
|||||||
Id = CoreHelpers.GenerateComb();
|
Id = CoreHelpers.GenerateComb();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the name of the provider, HTML decoded ready for display.
|
||||||
|
/// </summary>
|
||||||
|
public string DisplayName()
|
||||||
|
{
|
||||||
|
return WebUtility.HtmlDecode(Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the business name of the provider, HTML decoded ready for display.
|
||||||
|
/// </summary>
|
||||||
|
public string DisplayBusinessName()
|
||||||
|
{
|
||||||
|
return WebUtility.HtmlDecode(BusinessName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using System.Text.Json.Serialization;
|
||||||
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
namespace Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
|
|
||||||
@ -6,6 +8,7 @@ public class OrganizationUserOrganizationDetails
|
|||||||
{
|
{
|
||||||
public Guid OrganizationId { get; set; }
|
public Guid OrganizationId { get; set; }
|
||||||
public Guid? UserId { get; set; }
|
public Guid? UserId { get; set; }
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public bool UsePolicies { get; set; }
|
public bool UsePolicies { get; set; }
|
||||||
public bool UseSso { get; set; }
|
public bool UseSso { get; set; }
|
||||||
@ -37,6 +40,7 @@ public class OrganizationUserOrganizationDetails
|
|||||||
public string PublicKey { get; set; }
|
public string PublicKey { get; set; }
|
||||||
public string PrivateKey { get; set; }
|
public string PrivateKey { get; set; }
|
||||||
public Guid? ProviderId { get; set; }
|
public Guid? ProviderId { get; set; }
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string ProviderName { get; set; }
|
public string ProviderName { get; set; }
|
||||||
public ProviderType? ProviderType { get; set; }
|
public ProviderType? ProviderType { get; set; }
|
||||||
public string FamilySponsorshipFriendlyName { get; set; }
|
public string FamilySponsorshipFriendlyName { get; set; }
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using Bit.Core.Enums;
|
using System.Net;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.Models.Data.Provider;
|
namespace Bit.Core.AdminConsole.Models.Data.Provider;
|
||||||
|
|
||||||
@ -7,6 +10,10 @@ public class ProviderOrganizationOrganizationDetails
|
|||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public Guid ProviderId { get; set; }
|
public Guid ProviderId { get; set; }
|
||||||
public Guid OrganizationId { get; set; }
|
public Guid OrganizationId { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// This value is HTML encoded. For display purposes use the method DisplayName() instead.
|
||||||
|
/// </summary>
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string OrganizationName { get; set; }
|
public string OrganizationName { get; set; }
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
public string Settings { get; set; }
|
public string Settings { get; set; }
|
||||||
@ -16,4 +23,12 @@ public class ProviderOrganizationOrganizationDetails
|
|||||||
public int? Seats { get; set; }
|
public int? Seats { get; set; }
|
||||||
public string Plan { get; set; }
|
public string Plan { get; set; }
|
||||||
public OrganizationStatusType Status { get; set; }
|
public OrganizationStatusType Status { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the name of the organization, HTML decoded ready for display.
|
||||||
|
/// </summary>
|
||||||
|
public string DisplayName()
|
||||||
|
{
|
||||||
|
return WebUtility.HtmlDecode(OrganizationName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using System.Text.Json.Serialization;
|
||||||
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.Models.Data.Provider;
|
namespace Bit.Core.AdminConsole.Models.Data.Provider;
|
||||||
|
|
||||||
@ -7,6 +9,7 @@ public class ProviderOrganizationProviderDetails
|
|||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public Guid ProviderId { get; set; }
|
public Guid ProviderId { get; set; }
|
||||||
public Guid OrganizationId { get; set; }
|
public Guid OrganizationId { get; set; }
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string ProviderName { get; set; }
|
public string ProviderName { get; set; }
|
||||||
public ProviderType ProviderType { get; set; }
|
public ProviderType ProviderType { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using System.Text.Json.Serialization;
|
||||||
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.Models.Data.Provider;
|
namespace Bit.Core.AdminConsole.Models.Data.Provider;
|
||||||
|
|
||||||
@ -6,6 +8,7 @@ public class ProviderUserOrganizationDetails
|
|||||||
{
|
{
|
||||||
public Guid OrganizationId { get; set; }
|
public Guid OrganizationId { get; set; }
|
||||||
public Guid? UserId { get; set; }
|
public Guid? UserId { get; set; }
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public bool UsePolicies { get; set; }
|
public bool UsePolicies { get; set; }
|
||||||
public bool UseSso { get; set; }
|
public bool UseSso { get; set; }
|
||||||
@ -33,6 +36,7 @@ public class ProviderUserOrganizationDetails
|
|||||||
public string PrivateKey { get; set; }
|
public string PrivateKey { get; set; }
|
||||||
public Guid? ProviderId { get; set; }
|
public Guid? ProviderId { get; set; }
|
||||||
public Guid? ProviderUserId { get; set; }
|
public Guid? ProviderUserId { get; set; }
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string ProviderName { get; set; }
|
public string ProviderName { get; set; }
|
||||||
public Core.Enums.PlanType PlanType { get; set; }
|
public Core.Enums.PlanType PlanType { get; set; }
|
||||||
public bool LimitCollectionCreationDeletion { get; set; }
|
public bool LimitCollectionCreationDeletion { get; set; }
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using System.Text.Json.Serialization;
|
||||||
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.Models.Data.Provider;
|
namespace Bit.Core.AdminConsole.Models.Data.Provider;
|
||||||
|
|
||||||
@ -6,6 +8,7 @@ public class ProviderUserProviderDetails
|
|||||||
{
|
{
|
||||||
public Guid ProviderId { get; set; }
|
public Guid ProviderId { get; set; }
|
||||||
public Guid? UserId { get; set; }
|
public Guid? UserId { get; set; }
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
public ProviderUserStatusType Status { get; set; }
|
public ProviderUserStatusType Status { get; set; }
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using System.Text.Json.Serialization;
|
||||||
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Core.AdminConsole.Models.Data.Provider;
|
namespace Bit.Core.AdminConsole.Models.Data.Provider;
|
||||||
|
|
||||||
@ -7,6 +9,7 @@ public class ProviderUserUserDetails
|
|||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public Guid ProviderId { get; set; }
|
public Guid ProviderId { get; set; }
|
||||||
public Guid? UserId { get; set; }
|
public Guid? UserId { get; set; }
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
public ProviderUserStatusType Status { get; set; }
|
public ProviderUserStatusType Status { get; set; }
|
||||||
|
@ -828,7 +828,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
await customerService.UpdateAsync(organization.GatewayCustomerId, new CustomerUpdateOptions
|
await customerService.UpdateAsync(organization.GatewayCustomerId, new CustomerUpdateOptions
|
||||||
{
|
{
|
||||||
Email = organization.BillingEmail,
|
Email = organization.BillingEmail,
|
||||||
Description = organization.BusinessName
|
Description = organization.DisplayBusinessName()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1285,7 +1285,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
orgUser.Email = null;
|
orgUser.Email = null;
|
||||||
|
|
||||||
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Confirmed);
|
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Confirmed);
|
||||||
await _mailService.SendOrganizationConfirmedEmailAsync(organization.Name, user.Email);
|
await _mailService.SendOrganizationConfirmedEmailAsync(organization.DisplayName(), user.Email);
|
||||||
await DeleteAndPushUserRegistrationAsync(organizationId, user.Id);
|
await DeleteAndPushUserRegistrationAsync(organizationId, user.Id);
|
||||||
succeededUsers.Add(orgUser);
|
succeededUsers.Add(orgUser);
|
||||||
result.Add(Tuple.Create(orgUser, ""));
|
result.Add(Tuple.Create(orgUser, ""));
|
||||||
|
@ -131,7 +131,7 @@ public class PolicyService : IPolicyService
|
|||||||
await organizationService.DeleteUserAsync(policy.OrganizationId, orgUser.Id,
|
await organizationService.DeleteUserAsync(policy.OrganizationId, orgUser.Id,
|
||||||
savingUserId);
|
savingUserId);
|
||||||
await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
|
await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
|
||||||
org.Name, orgUser.Email);
|
org.DisplayName(), orgUser.Email);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -147,7 +147,7 @@ public class PolicyService : IPolicyService
|
|||||||
await organizationService.DeleteUserAsync(policy.OrganizationId, orgUser.Id,
|
await organizationService.DeleteUserAsync(policy.OrganizationId, orgUser.Id,
|
||||||
savingUserId);
|
savingUserId);
|
||||||
await _mailService.SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(
|
await _mailService.SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(
|
||||||
org.Name, orgUser.Email);
|
org.DisplayName(), orgUser.Email);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -15,7 +15,7 @@ public class OrganizationInvitesInfo
|
|||||||
bool initOrganization = false
|
bool initOrganization = false
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
OrganizationName = org.Name;
|
OrganizationName = org.DisplayName();
|
||||||
OrgSsoIdentifier = org.Identifier;
|
OrgSsoIdentifier = org.Identifier;
|
||||||
|
|
||||||
IsFreeOrg = org.PlanType == PlanType.Free;
|
IsFreeOrg = org.PlanType == PlanType.Free;
|
||||||
|
@ -65,6 +65,6 @@ public class SendSponsorshipOfferCommand : ISendSponsorshipOfferCommand
|
|||||||
throw new BadRequestException("Cannot find an outstanding sponsorship offer for this organization.");
|
throw new BadRequestException("Cannot find an outstanding sponsorship offer for this organization.");
|
||||||
}
|
}
|
||||||
|
|
||||||
await SendSponsorshipOfferAsync(sponsorship, sponsoringOrg.Name);
|
await SendSponsorshipOfferAsync(sponsorship, sponsoringOrg.DisplayName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ public class HandlebarsMailService : IMailService
|
|||||||
|
|
||||||
public async Task SendOrganizationAutoscaledEmailAsync(Organization organization, int initialSeatCount, IEnumerable<string> ownerEmails)
|
public async Task SendOrganizationAutoscaledEmailAsync(Organization organization, int initialSeatCount, IEnumerable<string> ownerEmails)
|
||||||
{
|
{
|
||||||
var message = CreateDefaultMessage($"{organization.Name} Seat Count Has Increased", ownerEmails);
|
var message = CreateDefaultMessage($"{organization.DisplayName()} Seat Count Has Increased", ownerEmails);
|
||||||
var model = new OrganizationSeatsAutoscaledViewModel
|
var model = new OrganizationSeatsAutoscaledViewModel
|
||||||
{
|
{
|
||||||
OrganizationId = organization.Id,
|
OrganizationId = organization.Id,
|
||||||
@ -160,7 +160,7 @@ public class HandlebarsMailService : IMailService
|
|||||||
|
|
||||||
public async Task SendOrganizationMaxSeatLimitReachedEmailAsync(Organization organization, int maxSeatCount, IEnumerable<string> ownerEmails)
|
public async Task SendOrganizationMaxSeatLimitReachedEmailAsync(Organization organization, int maxSeatCount, IEnumerable<string> ownerEmails)
|
||||||
{
|
{
|
||||||
var message = CreateDefaultMessage($"{organization.Name} Seat Limit Reached", ownerEmails);
|
var message = CreateDefaultMessage($"{organization.DisplayName()} Seat Limit Reached", ownerEmails);
|
||||||
var model = new OrganizationSeatsMaxReachedViewModel
|
var model = new OrganizationSeatsMaxReachedViewModel
|
||||||
{
|
{
|
||||||
OrganizationId = organization.Id,
|
OrganizationId = organization.Id,
|
||||||
@ -179,7 +179,7 @@ public class HandlebarsMailService : IMailService
|
|||||||
var model = new OrganizationUserAcceptedViewModel
|
var model = new OrganizationUserAcceptedViewModel
|
||||||
{
|
{
|
||||||
OrganizationId = organization.Id,
|
OrganizationId = organization.Id,
|
||||||
OrganizationName = CoreHelpers.SanitizeForEmail(organization.Name, false),
|
OrganizationName = CoreHelpers.SanitizeForEmail(organization.DisplayName(), false),
|
||||||
UserIdentifier = userIdentifier,
|
UserIdentifier = userIdentifier,
|
||||||
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
|
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
|
||||||
SiteName = _globalSettings.SiteName
|
SiteName = _globalSettings.SiteName
|
||||||
@ -933,7 +933,7 @@ public class HandlebarsMailService : IMailService
|
|||||||
public async Task SendSecretsManagerMaxSeatLimitReachedEmailAsync(Organization organization, int maxSeatCount,
|
public async Task SendSecretsManagerMaxSeatLimitReachedEmailAsync(Organization organization, int maxSeatCount,
|
||||||
IEnumerable<string> ownerEmails)
|
IEnumerable<string> ownerEmails)
|
||||||
{
|
{
|
||||||
var message = CreateDefaultMessage($"{organization.Name} Secrets Manager Seat Limit Reached", ownerEmails);
|
var message = CreateDefaultMessage($"{organization.DisplayName()} Secrets Manager Seat Limit Reached", ownerEmails);
|
||||||
var model = new OrganizationSeatsMaxReachedViewModel
|
var model = new OrganizationSeatsMaxReachedViewModel
|
||||||
{
|
{
|
||||||
OrganizationId = organization.Id,
|
OrganizationId = organization.Id,
|
||||||
@ -948,7 +948,7 @@ public class HandlebarsMailService : IMailService
|
|||||||
public async Task SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(Organization organization, int maxSeatCount,
|
public async Task SendSecretsManagerMaxServiceAccountLimitReachedEmailAsync(Organization organization, int maxSeatCount,
|
||||||
IEnumerable<string> ownerEmails)
|
IEnumerable<string> ownerEmails)
|
||||||
{
|
{
|
||||||
var message = CreateDefaultMessage($"{organization.Name} Secrets Manager Service Accounts Limit Reached", ownerEmails);
|
var message = CreateDefaultMessage($"{organization.DisplayName()} Secrets Manager Service Accounts Limit Reached", ownerEmails);
|
||||||
var model = new OrganizationServiceAccountsMaxReachedViewModel
|
var model = new OrganizationServiceAccountsMaxReachedViewModel
|
||||||
{
|
{
|
||||||
OrganizationId = organization.Id,
|
OrganizationId = organization.Id,
|
||||||
|
@ -132,13 +132,13 @@ public class LicensingService : ILicensingService
|
|||||||
{
|
{
|
||||||
_logger.LogInformation(Constants.BypassFiltersEventId, null,
|
_logger.LogInformation(Constants.BypassFiltersEventId, null,
|
||||||
"Organization {0} ({1}) has an invalid license and is being disabled. Reason: {2}",
|
"Organization {0} ({1}) has an invalid license and is being disabled. Reason: {2}",
|
||||||
org.Id, org.Name, reason);
|
org.Id, org.DisplayName(), reason);
|
||||||
org.Enabled = false;
|
org.Enabled = false;
|
||||||
org.ExpirationDate = license?.Expires ?? DateTime.UtcNow;
|
org.ExpirationDate = license?.Expires ?? DateTime.UtcNow;
|
||||||
org.RevisionDate = DateTime.UtcNow;
|
org.RevisionDate = DateTime.UtcNow;
|
||||||
await _organizationRepository.ReplaceAsync(org);
|
await _organizationRepository.ReplaceAsync(org);
|
||||||
|
|
||||||
await _mailService.SendLicenseExpiredAsync(new List<string> { org.BillingEmail }, org.Name);
|
await _mailService.SendLicenseExpiredAsync(new List<string> { org.BillingEmail }, org.DisplayName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ValidateUsersAsync()
|
public async Task ValidateUsersAsync()
|
||||||
|
@ -131,7 +131,7 @@ public class StripePaymentService : IPaymentService
|
|||||||
{
|
{
|
||||||
customer = await _stripeAdapter.CustomerCreateAsync(new Stripe.CustomerCreateOptions
|
customer = await _stripeAdapter.CustomerCreateAsync(new Stripe.CustomerCreateOptions
|
||||||
{
|
{
|
||||||
Description = org.BusinessName,
|
Description = org.DisplayBusinessName(),
|
||||||
Email = org.BillingEmail,
|
Email = org.BillingEmail,
|
||||||
Source = stipeCustomerSourceToken,
|
Source = stipeCustomerSourceToken,
|
||||||
PaymentMethod = stipeCustomerPaymentMethodId,
|
PaymentMethod = stipeCustomerPaymentMethodId,
|
||||||
|
@ -797,7 +797,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
user.ForcePasswordReset = true;
|
user.ForcePasswordReset = true;
|
||||||
|
|
||||||
await _userRepository.ReplaceAsync(user);
|
await _userRepository.ReplaceAsync(user);
|
||||||
await _mailService.SendAdminResetPasswordEmailAsync(user.Email, user.Name, org.Name);
|
await _mailService.SendAdminResetPasswordEmailAsync(user.Email, user.Name, org.DisplayName());
|
||||||
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_AdminResetPassword);
|
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_AdminResetPassword);
|
||||||
await _pushService.PushLogOutAsync(user.Id);
|
await _pushService.PushLogOutAsync(user.Id);
|
||||||
|
|
||||||
@ -1391,7 +1391,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
await organizationService.DeleteUserAsync(p.OrganizationId, user.Id);
|
await organizationService.DeleteUserAsync(p.OrganizationId, user.Id);
|
||||||
var organization = await _organizationRepository.GetByIdAsync(p.OrganizationId);
|
var organization = await _organizationRepository.GetByIdAsync(p.OrganizationId);
|
||||||
await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
|
await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
|
||||||
organization.Name, user.Email);
|
organization.DisplayName(), user.Email);
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
|
|
||||||
await Task.WhenAll(removeOrgUserTasks);
|
await Task.WhenAll(removeOrgUserTasks);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Net;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using NS = Newtonsoft.Json;
|
using NS = Newtonsoft.Json;
|
||||||
@ -192,3 +193,33 @@ public class PermissiveStringEnumerableConverter : JsonConverter<IEnumerable<str
|
|||||||
writer.WriteEndArray();
|
writer.WriteEndArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes incoming strings using HTML encoding
|
||||||
|
/// and decodes outgoing strings using HTML decoding.
|
||||||
|
/// </summary>
|
||||||
|
public class HtmlEncodingStringConverter : JsonConverter<string>
|
||||||
|
{
|
||||||
|
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonTokenType.String)
|
||||||
|
{
|
||||||
|
var originalValue = reader.GetString();
|
||||||
|
return WebUtility.HtmlEncode(originalValue);
|
||||||
|
}
|
||||||
|
return reader.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
var encodedValue = WebUtility.HtmlDecode(value);
|
||||||
|
writer.WriteStringValue(encodedValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteNullValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1744,7 +1744,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|||||||
await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, userService);
|
await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, userService);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Confirmed);
|
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Confirmed);
|
||||||
await sutProvider.GetDependency<IMailService>().Received(1).SendOrganizationConfirmedEmailAsync(org.Name, user.Email);
|
await sutProvider.GetDependency<IMailService>().Received(1).SendOrganizationConfirmedEmailAsync(org.DisplayName(), user.Email);
|
||||||
await organizationUserRepository.Received(1).ReplaceManyAsync(Arg.Is<List<OrganizationUser>>(users => users.Contains(orgUser) && users.Count == 1));
|
await organizationUserRepository.Received(1).ReplaceManyAsync(Arg.Is<List<OrganizationUser>>(users => users.Contains(orgUser) && users.Count == 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,7 +329,7 @@ public class PolicyServiceTests
|
|||||||
.DeleteUserAsync(policy.OrganizationId, orgUserDetail.Id, savingUserId);
|
.DeleteUserAsync(policy.OrganizationId, orgUserDetail.Id, savingUserId);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IMailService>().Received()
|
await sutProvider.GetDependency<IMailService>().Received()
|
||||||
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(org.Name, orgUserDetail.Email);
|
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(org.DisplayName(), orgUserDetail.Email);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IEventService>().Received()
|
await sutProvider.GetDependency<IEventService>().Received()
|
||||||
.LogPolicyEventAsync(policy, EventType.Policy_Updated);
|
.LogPolicyEventAsync(policy, EventType.Policy_Updated);
|
||||||
|
89
test/Core.Test/Utilities/HtmlEncodingStringConverterTests.cs
Normal file
89
test/Core.Test/Utilities/HtmlEncodingStringConverterTests.cs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Core.Test.Utilities;
|
||||||
|
|
||||||
|
public class HtmlEncodingStringConverterTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Serialize_WhenEncodedValueIsNotNull_SerializesHtmlEncodedString()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var obj = new HtmlEncodedString
|
||||||
|
{
|
||||||
|
EncodedValue = "This is <b>bold</b>",
|
||||||
|
NonEncodedValue = "This is <b>bold</b>"
|
||||||
|
};
|
||||||
|
const string expectedJsonString = "{\"EncodedValue\":\"This is <b>bold</b>\",\"NonEncodedValue\":\"This is <b>bold</b>\"}";
|
||||||
|
|
||||||
|
// This is necessary to prevent the serializer from double encoding the string
|
||||||
|
var serializerOptions = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var jsonString = JsonSerializer.Serialize(obj, serializerOptions);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedJsonString, jsonString);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Serialize_WhenEncodedValueIsNull_SerializesNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var obj = new HtmlEncodedString
|
||||||
|
{
|
||||||
|
EncodedValue = null,
|
||||||
|
NonEncodedValue = null
|
||||||
|
};
|
||||||
|
const string expectedJsonString = "{\"EncodedValue\":null,\"NonEncodedValue\":null}";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var jsonString = JsonSerializer.Serialize(obj);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedJsonString, jsonString);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Deserialize_WhenJsonContainsHtmlEncodedString_ReturnsDecodedString()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string json = "{\"EncodedValue\":\"This is <b>bold</b>\",\"NonEncodedValue\":\"This is <b>bold</b>\"}";
|
||||||
|
const string expectedEncodedValue = "This is <b>bold</b>";
|
||||||
|
const string expectedNonEncodedValue = "This is <b>bold</b>";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var obj = JsonSerializer.Deserialize<HtmlEncodedString>(json);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedEncodedValue, obj.EncodedValue);
|
||||||
|
Assert.Equal(expectedNonEncodedValue, obj.NonEncodedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Deserialize_WhenJsonContainsNull_ReturnsNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string json = "{\"EncodedValue\":null,\"NonEncodedValue\":null}";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var obj = JsonSerializer.Deserialize<HtmlEncodedString>(json);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(obj.EncodedValue);
|
||||||
|
Assert.Null(obj.NonEncodedValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HtmlEncodedString
|
||||||
|
{
|
||||||
|
[JsonConverter(typeof(HtmlEncodingStringConverter))]
|
||||||
|
public string EncodedValue { get; set; }
|
||||||
|
|
||||||
|
public string NonEncodedValue { get; set; }
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user