diff --git a/src/Business/Grand.Business.Catalog/Services/Brands/BrandService.cs b/src/Business/Grand.Business.Catalog/Services/Brands/BrandService.cs
index 465b32556..7e12b8a4d 100644
--- a/src/Business/Grand.Business.Catalog/Services/Brands/BrandService.cs
+++ b/src/Business/Grand.Business.Catalog/Services/Brands/BrandService.cs
@@ -57,8 +57,8 @@ public BrandService(ICacheBase cacheBase,
/// Page size
/// A value that indicates if it should shows hidden records
/// Brands
- public virtual async Task> GetAllBrands(string brandName = "",
- string storeId = "",
+ public virtual async Task> GetAllBrands(string brandName,
+ string storeId,
int pageIndex = 0,
int pageSize = int.MaxValue,
bool showHidden = false)
diff --git a/src/Business/Grand.Business.Catalog/Services/Categories/CategoryService.cs b/src/Business/Grand.Business.Catalog/Services/Categories/CategoryService.cs
index 46cee17da..906ed4e53 100644
--- a/src/Business/Grand.Business.Catalog/Services/Categories/CategoryService.cs
+++ b/src/Business/Grand.Business.Catalog/Services/Categories/CategoryService.cs
@@ -74,8 +74,8 @@ public CategoryService(ICacheBase cacheBase,
/// Page size
/// A value that indicates if it should shows hidden records
/// Categories
- public virtual async Task> GetAllCategories(string parentId = null, string categoryName = "",
- string storeId = "",
+ public virtual async Task> GetAllCategories(string parentId, string categoryName,
+ string storeId,
int pageIndex = 0, int pageSize = int.MaxValue, bool showHidden = false)
{
var query = from c in _categoryRepository.Table
diff --git a/src/Business/Grand.Business.Catalog/Services/Collections/CollectionService.cs b/src/Business/Grand.Business.Catalog/Services/Collections/CollectionService.cs
index db6a655ee..0175e0f78 100644
--- a/src/Business/Grand.Business.Catalog/Services/Collections/CollectionService.cs
+++ b/src/Business/Grand.Business.Catalog/Services/Collections/CollectionService.cs
@@ -61,8 +61,8 @@ public CollectionService(ICacheBase cacheBase,
/// Page size
/// A value that indicates if it should shows hidden records
/// Collections
- public virtual async Task> GetAllCollections(string collectionName = "",
- string storeId = "",
+ public virtual async Task> GetAllCollections(string collectionName,
+ string storeId,
int pageIndex = 0,
int pageSize = int.MaxValue,
bool showHidden = false)
diff --git a/src/Business/Grand.Business.Core/Interfaces/Catalog/Brands/IBrandService.cs b/src/Business/Grand.Business.Core/Interfaces/Catalog/Brands/IBrandService.cs
index bd2e353ce..11e09608f 100644
--- a/src/Business/Grand.Business.Core/Interfaces/Catalog/Brands/IBrandService.cs
+++ b/src/Business/Grand.Business.Core/Interfaces/Catalog/Brands/IBrandService.cs
@@ -17,8 +17,8 @@ public interface IBrandService
/// Page size
/// A value that indicates if it should shows hidden records
/// Brands
- Task> GetAllBrands(string brandName = "",
- string storeId = "",
+ Task> GetAllBrands(string brandName,
+ string storeId,
int pageIndex = 0,
int pageSize = int.MaxValue,
bool showHidden = false);
diff --git a/src/Business/Grand.Business.Core/Interfaces/Catalog/Categories/ICategoryService.cs b/src/Business/Grand.Business.Core/Interfaces/Catalog/Categories/ICategoryService.cs
index a6ce034fc..5015edb1a 100644
--- a/src/Business/Grand.Business.Core/Interfaces/Catalog/Categories/ICategoryService.cs
+++ b/src/Business/Grand.Business.Core/Interfaces/Catalog/Categories/ICategoryService.cs
@@ -15,7 +15,7 @@ public interface ICategoryService
/// Page size
/// A value that indicates if it should shows hidden records
/// Categories
- Task> GetAllCategories(string parentId = null, string categoryName = "", string storeId = "",
+ Task> GetAllCategories(string parentId, string categoryName, string storeId,
int pageIndex = 0, int pageSize = int.MaxValue, bool showHidden = false);
///
diff --git a/src/Business/Grand.Business.Core/Interfaces/Catalog/Collections/ICollectionService.cs b/src/Business/Grand.Business.Core/Interfaces/Catalog/Collections/ICollectionService.cs
index ecae8e334..fa1c18d63 100644
--- a/src/Business/Grand.Business.Core/Interfaces/Catalog/Collections/ICollectionService.cs
+++ b/src/Business/Grand.Business.Core/Interfaces/Catalog/Collections/ICollectionService.cs
@@ -17,8 +17,8 @@ public interface ICollectionService
/// Page size
/// A value that indicates if it should shows hidden records
/// Collections
- Task> GetAllCollections(string collectionName = "",
- string storeId = "",
+ Task> GetAllCollections(string collectionName,
+ string storeId,
int pageIndex = 0,
int pageSize = int.MaxValue,
bool showHidden = false);
diff --git a/src/Business/Grand.Business.Messages/Commands/Handlers/Common/GetSitemapXMLCommandHandler.cs b/src/Business/Grand.Business.Messages/Commands/Handlers/Common/GetSitemapXMLCommandHandler.cs
index 4b89039f8..ce42c9ecd 100644
--- a/src/Business/Grand.Business.Messages/Commands/Handlers/Common/GetSitemapXMLCommandHandler.cs
+++ b/src/Business/Grand.Business.Messages/Commands/Handlers/Common/GetSitemapXMLCommandHandler.cs
@@ -231,7 +231,7 @@ private async Task> GetCategoryUrls(string parentCategor
private async Task> GetBrandUrls(Language language, Store store)
{
- var brands = await _brandService.GetAllBrands(storeId: store.Id);
+ var brands = await _brandService.GetAllBrands(brandName: "", storeId: store.Id);
var brandUrls = new List();
var storeLocation = GetStoreLocation();
foreach (var brand in brands)
diff --git a/src/Tests/Grand.Business.Catalog.Tests/Services/Brands/BrandServiceTests.cs b/src/Tests/Grand.Business.Catalog.Tests/Services/Brands/BrandServiceTests.cs
index e49faab2e..a648d49e9 100644
--- a/src/Tests/Grand.Business.Catalog.Tests/Services/Brands/BrandServiceTests.cs
+++ b/src/Tests/Grand.Business.Catalog.Tests/Services/Brands/BrandServiceTests.cs
@@ -47,7 +47,7 @@ public async Task GetAllBrandsTest()
await _brandService.InsertBrand(new Brand { Published = true });
//Act
- var brand = await _brandService.GetAllBrands();
+ var brand = await _brandService.GetAllBrands(brandName: "", storeId: "");
//Assert
Assert.HasCount(3, brand);
diff --git a/src/Tests/Grand.Business.Catalog.Tests/Services/Categories/CategoryServiceDbTests.cs b/src/Tests/Grand.Business.Catalog.Tests/Services/Categories/CategoryServiceDbTests.cs
index bf8f0484a..03583a402 100644
--- a/src/Tests/Grand.Business.Catalog.Tests/Services/Categories/CategoryServiceDbTests.cs
+++ b/src/Tests/Grand.Business.Catalog.Tests/Services/Categories/CategoryServiceDbTests.cs
@@ -106,7 +106,7 @@ public async Task GetAllCategories()
{
var allCategory = GetMockCategoryList();
allCategory.ToList().ForEach(x => _categoryService.InsertCategory(x).GetAwaiter().GetResult());
- var result = await _categoryService.GetAllCategories();
+ var result = await _categoryService.GetAllCategories(parentId: null, categoryName: "", storeId: "");
Assert.HasCount(5, result);
}
diff --git a/src/Tests/Grand.Business.Common.Tests/Services/Security/AclServiceTest.cs b/src/Tests/Grand.Business.Common.Tests/Services/Security/AclServiceTest.cs
index 6d48b43ec..b59b1ae94 100644
--- a/src/Tests/Grand.Business.Common.Tests/Services/Security/AclServiceTest.cs
+++ b/src/Tests/Grand.Business.Common.Tests/Services/Security/AclServiceTest.cs
@@ -42,4 +42,22 @@ public void Authorize_ReturnTrue()
_accessControlConfig.IgnoreStoreLimitations = true;
Assert.IsTrue(_aclService.Authorize(product, "id"));
}
+
+ ///
+ /// Pins the fail-open: an empty store grants access even to an entity that is limited to other
+ /// stores. Callers therefore carry the whole burden of supplying the store - which is why the
+ /// list services no longer default that argument away.
+ ///
+ [TestMethod]
+ public void Authorize_WithoutAStore_GrantsAccessToAnEntityLimitedToOtherStores()
+ {
+ var product = new Product {
+ LimitedToStores = true,
+ Stores = { "another-store" }
+ };
+
+ Assert.IsFalse(_aclService.Authorize(product, "this-store"), "the store is not among the entity's stores");
+ Assert.IsTrue(_aclService.Authorize(product, ""), "fail-open: no store means no check");
+ Assert.IsTrue(_aclService.Authorize(product, (string)null), "fail-open: no store means no check");
+ }
}
\ No newline at end of file
diff --git a/src/Tests/Grand.Web.Admin.Tests/Extensions/AclMappingExtensionTests.cs b/src/Tests/Grand.Web.Admin.Tests/Extensions/AclMappingExtensionTests.cs
new file mode 100644
index 000000000..8f8d4c83f
--- /dev/null
+++ b/src/Tests/Grand.Web.Admin.Tests/Extensions/AclMappingExtensionTests.cs
@@ -0,0 +1,58 @@
+using Grand.Domain.Catalog;
+using Grand.Web.AdminShared.Extensions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Assert = Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
+
+namespace Grand.Web.Admin.Tests.Extensions;
+
+[TestClass]
+public class AclMappingExtensionTests
+{
+ ///
+ /// Pins the fail-open: an empty store grants access even to an entity limited to another store.
+ ///
+ [TestMethod]
+ public void AccessToEntityByStore_WithoutAStore_GrantsAccessToAnEntityLimitedToOtherStores()
+ {
+ var product = new Product {
+ LimitedToStores = true,
+ Stores = { "another-store" }
+ };
+
+ Assert.IsFalse(product.AccessToEntityByStore("this-store"));
+ Assert.IsTrue(product.AccessToEntityByStore(""), "fail-open: no store means no check");
+ }
+
+ ///
+ /// This check is stricter than AclService.Authorize on the same-sounding question: an entity
+ /// shared with a second store is refused here, while AclService grants it. Anyone reaching for
+ /// "does this store own the entity" has to pick deliberately between the two.
+ ///
+ [TestMethod]
+ public void AccessToEntityByStore_RefusesAnEntitySharedWithASecondStore()
+ {
+ var product = new Product {
+ LimitedToStores = true,
+ Stores = { "this-store", "another-store" }
+ };
+
+ Assert.IsFalse(product.AccessToEntityByStore("this-store"));
+ }
+
+ [TestMethod]
+ public void AccessToEntityByStore_AcceptsAnEntityOwnedByThatStoreAlone()
+ {
+ var product = new Product {
+ LimitedToStores = true,
+ Stores = { "this-store" }
+ };
+
+ Assert.IsTrue(product.AccessToEntityByStore("this-store"));
+ }
+
+ [TestMethod]
+ public void AccessToEntityByStore_RefusesAMissingEntity()
+ {
+ Assert.IsFalse(((Product)null).AccessToEntityByStore("this-store"));
+ }
+}
diff --git a/src/Web/Grand.Web.Admin/Controllers/BrandController.cs b/src/Web/Grand.Web.Admin/Controllers/BrandController.cs
index e0a1ad7eb..fb242820f 100644
--- a/src/Web/Grand.Web.Admin/Controllers/BrandController.cs
+++ b/src/Web/Grand.Web.Admin/Controllers/BrandController.cs
@@ -273,7 +273,7 @@ public async Task ExportXlsx([FromServices] IExportManager
{
try
{
- var bytes = await exportManager.Export(await _brandService.GetAllBrands(showHidden: true));
+ var bytes = await exportManager.Export(await _brandService.GetAllBrands(brandName: "", storeId: "", showHidden: true));
return File(bytes, "text/xls", "brands.xlsx");
}
catch (Exception exc)
diff --git a/src/Web/Grand.Web.Admin/Controllers/CategoryController.cs b/src/Web/Grand.Web.Admin/Controllers/CategoryController.cs
index 61cecf604..9b962438d 100644
--- a/src/Web/Grand.Web.Admin/Controllers/CategoryController.cs
+++ b/src/Web/Grand.Web.Admin/Controllers/CategoryController.cs
@@ -230,7 +230,7 @@ public async Task ExportXlsx([FromServices] IExportManager ExportXlsx([FromServices] IExportManager CategoryAddPopupList(DataSourceRequest command,
DiscountModel.AddCategoryToDiscountModel model, [FromServices] ICategoryService categoryService)
{
- var categories = await categoryService.GetAllCategories(categoryName: model.SearchCategoryName,
+ var categories = await categoryService.GetAllCategories(parentId: null, categoryName: model.SearchCategoryName, storeId: "",
pageIndex: command.Page - 1, pageSize: command.PageSize, showHidden: true);
var items = new List();
foreach (var item in categories)
diff --git a/src/Web/Grand.Web.Admin/Controllers/SearchController.cs b/src/Web/Grand.Web.Admin/Controllers/SearchController.cs
index 332e80e4b..4c649a990 100644
--- a/src/Web/Grand.Web.Admin/Controllers/SearchController.cs
+++ b/src/Web/Grand.Web.Admin/Controllers/SearchController.cs
@@ -90,7 +90,7 @@ public async Task Index(string searchTerm, FoundMenuItem[] foundM
if (result.Count < _adminSearchSettings.MaxSearchResultsCount && _adminSearchSettings.SearchInCategories)
{
- var categories = await _categoryService.GetAllCategories(categoryName: searchTerm,
+ var categories = await _categoryService.GetAllCategories(parentId: null, categoryName: searchTerm, storeId: "",
pageSize: _adminSearchSettings.MaxSearchResultsCount - result.Count, showHidden:
await _groupService.IsAdmin(_contextAccessor.WorkContext.CurrentCustomer));
foreach (var category in categories)
@@ -104,7 +104,7 @@ public async Task Index(string searchTerm, FoundMenuItem[] foundM
if (result.Count < _adminSearchSettings.MaxSearchResultsCount && _adminSearchSettings.SearchInCollections)
{
- var collections = await _collectionService.GetAllCollections(searchTerm,
+ var collections = await _collectionService.GetAllCollections(searchTerm, storeId: "",
pageSize: _adminSearchSettings.MaxSearchResultsCount - result.Count, showHidden: true);
foreach (var collection in collections)
result.Add(new Tuple