Skip to content

Commit

Permalink
Merge pull request #8 from Kentico/feat/security_review_changes
Browse files Browse the repository at this point in the history
Feat/security review changes
  • Loading branch information
michalJakubis authored May 31, 2024
2 parents 14ebb95 + 9a15b59 commit b9c6f7e
Show file tree
Hide file tree
Showing 23 changed files with 236 additions and 75 deletions.
4 changes: 2 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
<PackageVersion Include="kentico.xperience.imageprocessing" Version="29.0.2" />
<PackageVersion Include="Kentico.Xperience.Core" Version="29.0.2" />
<PackageVersion Include="Moq.AutoMock" Version="3.5.0" />
<PackageVersion Include="ShopifySharp" Version="6.13.0" />
<PackageVersion Include="ShopifySharp.Extensions.DependencyInjection" Version="1.4.0" />
<PackageVersion Include="ShopifySharp" Version="6.17.0" />
<PackageVersion Include="ShopifySharp.Extensions.DependencyInjection" Version="1.6.0" />
<PackageVersion Include="Microsoft.Extensions.ApiDescription.Client" Version="7.0.2" />
<PackageVersion Include="NSwag.ApiDescription.Client" Version="13.18.2" />
<PackageVersion Include="SonarAnalyzer.CSharp" Version="9.17.0.82934" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public async Task<IActionResult> Index(UpdateCartModel updateCartModel, CartOper
};

var result = cartOperation == CartOperation.Remove
? await shoppingService.RemoveCartItem(cartItemParams.MerchandiseID)
? await shoppingService.RemoveProductVariantFromCart(cartItemParams.MerchandiseID)
: await shoppingService.AddItemToCart(cartItemParams);

TempData[ERROR_MESSAGES_KEY] = result.ErrorMessages.ToArray();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,18 @@ public async Task<IActionResult> Index()
[HttpPost]
[Route("/cart/update")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Update([FromForm] string variantGraphQLId, [FromForm] int quantity, [FromForm] string cartOperation)
public async Task<IActionResult> Update([FromForm] string cartItemId, [FromForm] int quantity, [FromForm] string cartOperation)
{
var country = ShopifySharp.GraphQL.CountryCode.CZ;
if (Enum.TryParse<CartOperation>(cartOperation, out var operationEnum))
{
var result = operationEnum == CartOperation.Remove
? await shoppingService.RemoveCartItem(variantGraphQLId)
? await shoppingService.RemoveCartItem(cartItemId)
: await shoppingService.UpdateCartItem(new ShoppingCartItemParameters()
{
Country = country,
Quantity = quantity,
MerchandiseID = variantGraphQLId
ShoppingCartItemID = cartItemId
});

AddErrorsToTempData(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public record ProductDetailViewModel
public static ProductDetailViewModel GetViewModel(ProductDetailPage page, string selectedVariantID, string country, string currency, string[] errorMessages)
{
var product = page.Product.First();
var selectedVariant = product.Variants.FirstOrDefault(x => x.ShopifyVariantID == selectedVariantID) ?? product.Variants.First();
var selectedVariant = product.Variants.FirstOrDefault(x => x.ShopifyVariantID.Equals(selectedVariantID, StringComparison.Ordinal)) ?? product.Variants.First();

var allImages = product.Images.Concat(product.Variants.Select(x => x.Image.FirstOrDefault()))
.Where(x => x != null)
Expand All @@ -48,7 +48,7 @@ public static ProductDetailViewModel GetViewModel(ProductDetailPage page, string
Images = allImages,
DescriptionHTML = product.Description,
ParametersSection = product.Parameters,
Variants = product.Variants.Select(x => new SelectListItem(x.Title, x.ShopifyVariantID, x.ShopifyVariantID == selectedVariant.ShopifyVariantID)).ToList(),
Variants = product.Variants.Select(x => new SelectListItem(x.Title, x.ShopifyVariantID, x.ShopifyVariantID.Equals(selectedVariant.ShopifyVariantID, StringComparison.Ordinal))).ToList(),
SelectedShopifyVariantId = selectedVariant.ShopifyVariantID,
ShopifyProductId = product.ShopifyProductID,
CountryCode = country,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
@Html.Kentico().PageData()
<div class="cart-item-amount">
<span>@HtmlLocalizer["Qty"]</span>
<input type="hidden" name="@nameof(ShoppingCartItemViewModel.VariantGraphQLId)" value="@Model.VariantGraphQLId" />
<input type="hidden" name="@nameof(ShoppingCartItemViewModel.CartItemId)" value="@Model.CartItemId" />
<input type="number" class="add-to-cart-textbox" min="0" , name="@nameof(ShoppingCartItemViewModel.Quantity)" value="@Model.Quantity" />
<input type="submit" name="cartOperation" value="Update" class="btn btn-default" />
<input type="submit" name="cartOperation" value="Remove" class="btn btn-default" />
Expand Down
8 changes: 7 additions & 1 deletion examples/DancingGoat-Shopify/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,11 @@
}
},
"AllowedHosts": "*",
"CMSHashStringSalt": "9f38667d-c99f-43bb-9a92-043ce36ecb5d"
"CMSHashStringSalt": "9f38667d-c99f-43bb-9a92-043ce36ecb5d",
"CMSShopifyConfig": {
"ShopifyUrl": "https://your-shopify-store-url.com/",
"AdminApiToken": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"StorefrontApiToken": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"StorefrontApiVersion": "YYYY-MM"
}
}
20 changes: 10 additions & 10 deletions examples/DancingGoat-Shopify/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@
},
"ShopifySharp": {
"type": "Direct",
"requested": "[6.13.0, )",
"resolved": "6.13.0",
"contentHash": "GwM1K0FzTo9BrXSqG9hWxIOGGfhrbBfLXK+b6vMqaabmw0Uv4/n9gskQKSZ9CIB7vJksI4Grn+Tk9qW/dVqBJQ==",
"requested": "[6.17.0, )",
"resolved": "6.17.0",
"contentHash": "0yvFrbdvCaQyWHOXsd5Lp15mPpJDQ0G+PE8vJVRkMiH39nrEemI1i2UYrFTS8axgNndGJzB+E1XzctwELp7uug==",
"dependencies": {
"Microsoft.Extensions.Http": "2.1.0",
"System.Text.Json": "7.0.3",
Expand All @@ -66,11 +66,11 @@
},
"ShopifySharp.Extensions.DependencyInjection": {
"type": "Direct",
"requested": "[1.4.0, )",
"resolved": "1.4.0",
"contentHash": "jpaU4WpqjAXm/Tm7vQwC/gBbsdIHa4taO/aAN6BZxT8t91f9+OHkWZzFOsvChVUNn61cShcTR84tUfrv6X2cVg==",
"requested": "[1.6.0, )",
"resolved": "1.6.0",
"contentHash": "Hf0MexgLD6FbTcm81lFMPsw5kWK4XNEl7FPmYuoE9kPlWhWwR/wUWMYUpn6uSBTHCE+62yI2l+i7lsOb0zASnA==",
"dependencies": {
"ShopifySharp": "6.13.0",
"ShopifySharp": "6.16.0",
"microsoft.extensions.dependencyinjection": "8.0.0"
}
},
Expand Down Expand Up @@ -1015,16 +1015,16 @@
"Kentico.Xperience.Admin": "[29.0.2, )",
"Kentico.Xperience.Core": "[29.0.2, )",
"Kentico.Xperience.Ecommerce.Common": "[1.0.0-prerelease-1, )",
"ShopifySharp": "[6.13.0, )",
"ShopifySharp.Extensions.DependencyInjection": "[1.4.0, )",
"ShopifySharp": "[6.17.0, )",
"ShopifySharp.Extensions.DependencyInjection": "[1.6.0, )",
"System.Configuration.ConfigurationManager": "[8.0.0, )",
"System.Linq.Async": "[6.0.1, )"
}
},
"kentico.xperience.shopify.rcl": {
"type": "Project",
"dependencies": {
"Kentico.Xperience.Shopify": "[1.0.0-prerelease-1, )"
"Kentico.Xperience.Shopify": "[0.1.0-prerelease-1, )"
}
},
"GraphQL": {
Expand Down
18 changes: 9 additions & 9 deletions src/Kentico.Xperience.Shopify.Rcl/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -885,8 +885,8 @@
"Kentico.Xperience.Admin": "[29.0.2, )",
"Kentico.Xperience.Core": "[29.0.2, )",
"Kentico.Xperience.Ecommerce.Common": "[1.0.0-prerelease-1, )",
"ShopifySharp": "[6.13.0, )",
"ShopifySharp.Extensions.DependencyInjection": "[1.4.0, )",
"ShopifySharp": "[6.17.0, )",
"ShopifySharp.Extensions.DependencyInjection": "[1.6.0, )",
"System.Configuration.ConfigurationManager": "[8.0.0, )",
"System.Linq.Async": "[6.0.1, )"
}
Expand Down Expand Up @@ -974,9 +974,9 @@
},
"ShopifySharp": {
"type": "CentralTransitive",
"requested": "[6.13.0, )",
"resolved": "6.13.0",
"contentHash": "GwM1K0FzTo9BrXSqG9hWxIOGGfhrbBfLXK+b6vMqaabmw0Uv4/n9gskQKSZ9CIB7vJksI4Grn+Tk9qW/dVqBJQ==",
"requested": "[6.17.0, )",
"resolved": "6.17.0",
"contentHash": "0yvFrbdvCaQyWHOXsd5Lp15mPpJDQ0G+PE8vJVRkMiH39nrEemI1i2UYrFTS8axgNndGJzB+E1XzctwELp7uug==",
"dependencies": {
"Microsoft.Extensions.Http": "2.1.0",
"System.Text.Json": "7.0.3",
Expand All @@ -985,11 +985,11 @@
},
"ShopifySharp.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[1.4.0, )",
"resolved": "1.4.0",
"contentHash": "jpaU4WpqjAXm/Tm7vQwC/gBbsdIHa4taO/aAN6BZxT8t91f9+OHkWZzFOsvChVUNn61cShcTR84tUfrv6X2cVg==",
"requested": "[1.6.0, )",
"resolved": "1.6.0",
"contentHash": "Hf0MexgLD6FbTcm81lFMPsw5kWK4XNEl7FPmYuoE9kPlWhWwR/wUWMYUpn6uSBTHCE+62yI2l+i7lsOb0zASnA==",
"dependencies": {
"ShopifySharp": "6.13.0",
"ShopifySharp": "6.16.0",
"microsoft.extensions.dependencyinjection": "8.0.0"
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public ShopifyOrderService(

var result = await orderService.ListAsync(filter);

return result.Items.FirstOrDefault(x => x.SourceIdentifier == sourceId);
return result.Items.FirstOrDefault(x => x.SourceIdentifier.Equals(sourceId, StringComparison.Ordinal));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ private async Task<IDictionary<string, ProductPriceModel>> GetProductsPriceInter
continue;
}

var prices = product.Variants.Select(x => x.PresentmentPrices.FirstOrDefault(x => x.Price.CurrencyCode == currency));
var prices = product.Variants.Select(x => x.PresentmentPrices.FirstOrDefault(x => x.Price.CurrencyCode.Equals(currency, StringComparison.Ordinal)));

dict.TryAdd(product.Id.Value.ToString(), new ProductPriceModel()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ private async Task<Dictionary<string, ProductVariantListModel>> GetProductVarian
{
foreach (var variant in variants.Values)
{
var cartItem = cart.Items.FirstOrDefault(x => x.VariantGraphQLId == variant.MerchandiseID);
var cartItem = cart.Items.FirstOrDefault(x => x.VariantGraphQLId.Equals(variant.MerchandiseID, StringComparison.Ordinal));
variant.ItemsInCart = cartItem?.Quantity ?? 0;
}
}
Expand Down Expand Up @@ -163,7 +163,7 @@ private ListResultWrapper<ShopifyProductListModel> CreateResultModel(ListResult<
if (variants.Count() == 1)
{
var onlyVariant = variants.First();
var currencyPrice = onlyVariant.PresentmentPrices?.FirstOrDefault(x => x.Price.CurrencyCode == currency);
var currencyPrice = onlyVariant.PresentmentPrices?.FirstOrDefault(x => x.Price.CurrencyCode.Equals(currency, StringComparison.Ordinal));

return currencyPrice is { Price: not null } ?
(currencyPrice.Price.Amount, currencyPrice.CompareAtPrice?.Amount) : (null, null);
Expand All @@ -173,7 +173,7 @@ private ListResultWrapper<ShopifyProductListModel> CreateResultModel(ListResult<

foreach (var variant in variants)
{
var currencyPrice = variant.PresentmentPrices?.FirstOrDefault(x => x.Price.CurrencyCode == currency);
var currencyPrice = variant.PresentmentPrices?.FirstOrDefault(x => x.Price.CurrencyCode.Equals(currency, StringComparison.Ordinal));

if (currencyPrice?.Price.Amount != null)
{
Expand Down
6 changes: 6 additions & 0 deletions src/Kentico.Xperience.Shopify/ShopifyConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,11 @@ public static class ShopifyConstants
/// The default name for product variants.
/// </summary>
public const string DEFAULT_VARIANT_NAME = "default title";


/// <summary>
/// The name of the header where buyer IP address should be added.
/// </summary>
public const string STOREFRONT_API_BUYER_IP_NAME = "Shopify-Storefront-Buyer-IP";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,23 @@
using GraphQL.Client.Http;
using GraphQL.Client.Serializer.Newtonsoft;

using Microsoft.AspNetCore.Http;

namespace Kentico.Xperience.Shopify.ShoppingCart
{
internal class GraphQLHttpClientFactory : IGraphQLHttpClientFactory
{
private readonly HttpClient httpClient;

public GraphQLHttpClientFactory(IHttpClientFactory httpClientFactory)
public GraphQLHttpClientFactory(IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor)
{
httpClient = httpClientFactory.CreateClient(ShopifyConstants.STOREFRONT_API_CLIENT_NAME);
string? buyerIP = httpContextAccessor.HttpContext?.Connection?.RemoteIpAddress?.ToString();

if (!string.IsNullOrEmpty(buyerIP))
{
httpClient.DefaultRequestHeaders.Add(ShopifyConstants.STOREFRONT_API_BUYER_IP_NAME, buyerIP);
}
}

/// <inheritdoc/>
Expand Down
12 changes: 10 additions & 2 deletions src/Kentico.Xperience.Shopify/ShoppingCart/IShoppingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,20 @@ public interface IShoppingService
Task<CartOperationResult> UpdateCartItem(ShoppingCartItemParameters parameters);


/// <summary>
/// Remove all shopping cart items of specific product variant.
/// </summary>
/// <param name="variantGraphQLId">Product variant graphQL ID.</param>
/// <returns><see cref="CartOperationResult"/> with updated shopping cart if operation was successful.</returns>
Task<CartOperationResult> RemoveProductVariantFromCart(string variantGraphQLId);


/// <summary>
/// Remove shopping cart item.
/// </summary>
/// <param name="merchandiseId">Shopify product variant ID</param>
/// <param name="cartItemId">Shopify shopping cart item ID</param>
/// <returns><see cref="CartOperationResult"/> with updated shopping cart if operation was successful.</returns>
Task<CartOperationResult> RemoveCartItem(string merchandiseId);
Task<CartOperationResult> RemoveCartItem(string cartItemId);


/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ public class ShoppingCartItemParameters
public string MerchandiseID { get; set; } = string.Empty;


/// <summary>
/// ID of the shopping cart item if item was already in shopping cart.
/// If the cart item is new, the value will be empty string.
/// </summary>
public string ShoppingCartItemID { get; set; } = string.Empty;


/// <summary>
/// Country code for the item.
/// </summary>
Expand Down
43 changes: 39 additions & 4 deletions src/Kentico.Xperience.Shopify/ShoppingCart/ShoppingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ public async Task<CartOperationResult> UpdateCartItem(ShoppingCartItemParameters
{
var cart = await GetCurrentShoppingCart();

var cartItemToUpdate = cart?.Items.FirstOrDefault(x => x.ShopifyCartItemId == parameters.MerchandiseID);
var cartItemToUpdate = cart?.Items.FirstOrDefault(x => x.ShopifyCartItemId.Equals(parameters.ShoppingCartItemID, StringComparison.Ordinal));
if (cart == null || cartItemToUpdate == null)
{
return await AddItemToCart(parameters);
}
if (cartItemToUpdate.Quantity == parameters.Quantity)
{
return new CartOperationResult(cart, true);
}

int quantity = Math.Max(parameters.Quantity, 0);
var result = await UpdateCartItemInternal(cart.CartId, cartItemToUpdate, quantity);
Expand All @@ -62,15 +66,46 @@ public async Task<CartOperationResult> UpdateCartItem(ShoppingCartItemParameters
}


public async Task<CartOperationResult> RemoveCartItem(string merchandiseId)
public async Task<CartOperationResult> RemoveProductVariantFromCart(string variantGraphQLId)
{
var cart = await GetCurrentShoppingCart();
if (cart == null)
{
return new CartOperationResult(null, true);
}

var shopifyCartLines = cart.Items.Where(x => x.VariantGraphQLId.Equals(variantGraphQLId, StringComparison.Ordinal));
bool success = true;
CartOperationResult? result = null;

foreach (var shopifyCartLine in shopifyCartLines)
{
result = await RemoveCartItem(shopifyCartLine.ShopifyCartItemId);
if (!result.Success)
{
success = false;
}
}

// There was no cart item with given variant graphQL ID
if (result == null)
{
return new CartOperationResult(cart, success);
}

return new CartOperationResult(result.Cart, success);
}


public async Task<CartOperationResult> RemoveCartItem(string cartItemId)
{
var cart = await GetCurrentShoppingCart();
if (cart == null)
{
return new CartOperationResult(null, true);
}

var shopifyCartLine = cart.Items.FirstOrDefault(x => x.VariantGraphQLId == merchandiseId);
var shopifyCartLine = cart.Items.FirstOrDefault(x => x.ShopifyCartItemId.Equals(cartItemId, StringComparison.Ordinal));
if (shopifyCartLine == null)
{
return new CartOperationResult(cart, true);
Expand Down Expand Up @@ -166,7 +201,7 @@ public async Task<CartOperationResult> AddItemToCart(ShoppingCartItemParameters
StoreCartToCookiesAndSession(cart.CartId);
}

var addedItem = cart.Items.FirstOrDefault(x => x.VariantGraphQLId == parameters.MerchandiseID);
var addedItem = cart.Items.FirstOrDefault(x => x.VariantGraphQLId.Equals(parameters.MerchandiseID));
activityLogger.LogProductAddedToShoppingCartActivity(addedItem, parameters.Quantity);
}
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ protected IEnumerable<Guid> OrderItemsByShopify(IEnumerable<IContentItemBase> co
{
foreach (var shopifyObject in shopifyObjects.Where(x => x.Id.HasValue))
{
var contentItem = contentItems.FirstOrDefault(x => x.ShopifyObjectID == (shopifyObject.Id?.ToString() ?? string.Empty));
var contentItem = contentItems.FirstOrDefault(x => x.ShopifyObjectID.Equals(shopifyObject.Id?.ToString() ?? string.Empty, StringComparison.Ordinal));
yield return contentItem?.SystemFields.ContentItemGUID ?? Guid.Empty;
}
}
Expand Down
Loading

0 comments on commit b9c6f7e

Please sign in to comment.