better-route: Resource authorization
SkillSecurityConfigure better-route 1.1 Resource action and field authorization. Use for ResourcePolicy::publicReadPrivateWrite, adminOnly, capabilities, callbacks, Resource::policy, permissionCallback, per-action rules, wildcard rules, fieldPolicy, public Resource OpenAPI security, ownership policies, or reviewing CPT/table CRUD permissions. In 1.1 denied fields are rejected rather than silently stripped.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the better-route: Resource authorization skill
What this skill tells your AI
The instructions your AI receives, as published by lonsdale201/wp-agent-skills in better-route/br-resource-policy/SKILL.md and read by ahel’s review.
Use a Resource policy to decide who may call each generated action. Use fieldPolicy separately to authorize individual incoming fields.
Presets
use BetterRoute\Resource\ResourcePolicy;
->policy(ResourcePolicy::publicReadPrivateWrite('edit_posts'))
->policy(ResourcePolicy::adminOnly('manage_options'))
->policy(ResourcePolicy::capabilities([
'list' => 'read_private_reports',
'get' => 'read_private_reports',
'create' => ['edit_posts', 'manage_woocommerce'], // any-of
'update' => 'edit_posts',
'delete' => 'delete_posts',
]))
->policy(ResourcePolicy::callbacks([
'update' => static fn ($request, string $action): bool =>
current_user_can('edit_post', (int) $request->get_param('id')),
]))
Supported action keys are list, get, create, update, delete, and fallback *. update covers PUT and PATCH.
Rule values:
- boolean: allow/deny;
- non-empty capability string:
current_user_can($capability); - capability list: any capability may pass;
- callable: invoked with as many of
($request, $action, $resource)as its signature accepts.
A top-level permissionCallback callable overrides per-action resolution for the Resource. A top-level public => true opens every registered action, including writes; use it only with a deliberately read-only allow(['list', 'get']) Resource.
Defaults
Without a policy:
- table resources deny every action;
- CPT writes deny;
- CPT list/get allow only when the post type is publicly viewable, followed by item-level status/public/password visibility checks.
Prefer explicit policy even where the CPT default is safe, so intent and OpenAPI are clear.
Field policy in 1.1
->fieldPolicy([
'featured' => ['write' => 'manage_options'],
'author' => ['write' => ['edit_others_posts', 'manage_options']],
'external_id' => ['write' => false],
'tenant_id' => ['write' => static function (
$request,
string $field,
string $action,
?int $id
): bool {
return can_write_tenant($request, $id);
}],
])
Denied fields are not silently stripped in 1.1:
- boolean
falsereturns400 validation_failedwith a field error; - a failed capability/list/callback returns
403 forbiddenwith field error details; - allowed fields continue into coercion/sanitization.
A field-policy callable can accept ($request, $field, $action, $id, $resource). Declare only the prefix needed. The action is create or update; PATCH also resolves as update.
Only payload fields in the Resource's writable fields() set reach field policy. Unknown/read-only fields fail earlier.
OpenAPI
An action whose resolved static policy is explicitly public (true or top-level public) gets operation security: []. Callable policy source is never serialized into metadata; it is represented only as a safe callback marker where applicable.
Do not assume a callable's runtime result can be inferred into OpenAPI. Document the security scheme explicitly when middleware/auth is required.
Ownership
Use OwnedResourcePolicy::currentUserOwns() for get/update/delete ownership checks. It resolves resource ID from the request, compares the owner to native current WP user, and optionally permits an admin bypass capability. List access still needs query-level row filtering; a permission callback alone cannot remove other users' rows from a collection.
Review checklist
- Declare which actions exist with
allow()before reviewing permissions. - Never use top-level
public => trueon a write-capable Resource unintentionally. - Treat capability arrays as any-of, not all-of.
- Add object/row-level checks for get/update/delete and query-level filters for lists.
- Test denied fields and assert 400/403; do not expect silent removal.
- Keep policy callback work cheap because WordPress evaluates permission before the handler.
- Verify explicitly public actions emit
security: []in OpenAPI.
Related skills
- Use
br-resource-cptorbr-resource-tablefor source behavior. - Use
br-owned-resource-guardsfor ownership patterns. - Use
br-write-schemafor validation after field authorization.
References
- Verified source paths:
src/Resource/ResourcePolicy.phpsrc/Resource/Resource.phpsrc/Resource/OwnedResourcePolicy.php
Signals
- GitHub stars
- 22
- Forks
- 2
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
br-resource-policy- Source
- github.com/lonsdale201/wp-agent-skills