Skip to content

Move the request-customer Authorize overloads to Web - #941

Merged
KrzysztofPajak merged 3 commits into
developfrom
refactor/business-explicity-authorize
Oct 18, 2026
Merged

KrzysztofPajak merged 3 commits into
developfrom
refactor/business-explicity-authorize

Conversation

@KrzysztofPajak

@KrzysztofPajak KrzysztofPajak commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Part of the explicit-context work in Business (after #930–#940)
Type: feature

Issue

IPermissionService.Authorize(Permission) and Authorize(string) authorize the customer of the current request. They sat on the Business interface, and PermissionService injected IContextAccessor only to serve them, so nothing stopped Business code from calling them. Outside a request (scheduled task, background handler) such a check uses a null or wrong customer. #937 documented these overloads as the last accepted remainder of M1; #940 moved the matching GetResource(name) overload to Web.

Solution

  • IPermissionService (Grand.Business.Core) keeps only the overloads that take a Customer. PermissionService no longer takes IContextAccessor, so src/Business has no ambient read of the request left (the M1 grep IContextAccessor|IWorkContext over src/Business returns nothing).
  • New IRequestPermissionService in Grand.Web.Common.Security.Authorization with only Authorize(Permission) and Authorize(string). RequestPermissionService wraps IPermissionService and passes WorkContext?.CurrentCustomer; a request without a work context is not authorized.
  • Web filters (AuthorizeAdmin/Store/Vendor/AdminOrStore, PublicStore, ClosedStore, PermissionAuthorize), SiteMap, the admin panels (Admin, Store, Vendor, AdminShared), Grand.Web, Razor views and plugins inject IRequestPermissionService for request-customer checks. Code that already passes a customer, or reads/manages permissions, keeps IPermissionService (a class needing both injects both, e.g. CustomerGroupAclUpdateValidator). PermissionAuthorizeAction resolves the customer once and uses IPermissionService for both checks.
  • Grand.Module.Api does not reference Grand.Web.Common. BaseApiController exposes CurrentCustomer (work-context customer resolved from HttpContext.RequestServices), and API controllers pass it to IPermissionService.Authorize(..., CurrentCustomer); their constructors are unchanged. AuthorizeApiAdminAttribute takes IContextAccessor.
  • Authorize(string, Customer) returns false for a null customer instead of throwing, as Authorize(Permission, Customer) already did.
  • .ai/ guides describe the new contract.

Breaking changes

  • IPermissionService.Authorize(Permission) and Authorize(string) are removed. Plugins call them on IRequestPermissionService (Grand.Web.Common.Security.Authorization) or pass a customer. A plugin built against the old interface fails with MissingMethodException until rebuilt.
  • PermissionService constructor: (IRepository<Permission>, IRepository<PermissionAction>, IGroupService, ICacheBase); IContextAccessor removed.
  • Admin/Store/Vendor/AdminShared base controllers and view components take IRequestPermissionService instead of IPermissionService.

Testing

Automated: the solution builds, and dotnet test GrandNode.slnx passes. Grand.Business.Customers.Tests and Grand.Business.Marketing.Tests failed in the parallel full run and pass on their own (known parallel flake). New RequestPermissionServiceTests cover the request customer for both overloads and a request without a work context; PermissionServiceTests use an explicit customer and cover a null customer; AuthorizeApiAdminAttributeTests check the API filter authorizes the work-context customer. ContentPreviewTests swap the mock to IRequestPermissionService and also complete the controller setup (IPageHeadBuilder, Url, TempData) the blog preview path needs.

Manual (not run yet):

  1. Log in to Admin, Store owner and Vendor panels: the menus show only the permitted items, and a page without permission redirects to Access denied.
  2. Storefront as a guest and as a registered customer: cart and wishlist links follow the Enable shopping cart / wishlist permissions; with the store closed or not public, access follows the settings.
  3. Call the API (/api/... with a token) as an admin and as a customer without the API/admin permission: 200 vs 403.

IPermissionService (Business) now only authorizes a customer the caller
passes in, and PermissionService no longer takes IContextAccessor.

The new IRequestPermissionService : IPermissionService in
Grand.Web.Common.Security.Authorization adds Authorize(Permission) and
Authorize(string) for the customer of the current request. Web, the
admin panels, Razor views, filters and plugins inject it; the calls are
unchanged. Grand.Module.Api has no Web.Common reference, so its
controllers and AuthorizeApiAdminAttribute pass
WorkContext.CurrentCustomer explicitly.

Authorize(string, Customer) returns false for a null customer instead
of throwing, matching Authorize(Permission, Customer).

Breaking for external plugins that call the single-argument overloads
through IPermissionService.
Copilot AI balanced review requested due to automatic review settings October 18, 2026 06:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

support added 2 commits October 18, 2026 09:14
IRequestPermissionService no longer extends IPermissionService, so
request-side callers cannot create, update or delete permissions.
RequestPermissionService delegates to the registered IPermissionService
instead of inheriting PermissionService, so a replaced or decorated
permission service also applies to Web filters, views and components.

PermissionAuthorizeAction and CustomerGroupAclUpdateValidator take
IPermissionService for AuthorizeAction and GetPermissionById, and a
missing customer now denies access instead of throwing.

API controllers read the caller through BaseApiController.CurrentCustomer
rather than injecting IContextAccessor each, and the admin API filter
tolerates a missing work context.
@KrzysztofPajak
KrzysztofPajak merged commit b788f03 into develop Oct 18, 2026
7 of 8 checks passed
@KrzysztofPajak
KrzysztofPajak deleted the refactor/business-explicity-authorize branch October 18, 2026 10:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants