Download the PHP package wpboilerplate/wpb-access-control without Composer
On this page you can find all versions of the php package wpboilerplate/wpb-access-control. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download wpboilerplate/wpb-access-control
More information about wpboilerplate/wpb-access-control
Files in wpboilerplate/wpb-access-control
Package wpb-access-control
Short Description Extensible per-resource access control library for WordPress plugins
License GPL-2.0-or-later
Homepage https://github.com/WPBoilerplate/wpb-access-control
Informations about the package wpb-access-control
wpb-access-control
Extensible per-resource access control library for WordPress plugins.
Answers one question: "Does this user have access to this resource?"
The library owns its own database table (managed by BerlinDB), ships WordPress role and user providers out of the box, exposes a REST API for managing rules from any client, and provides a ready-to-drop-in React component so consuming plugins get a full admin UI without writing any front-end code.
Table of Contents
- Requirements
- Installation
- PHP Setup
- Complete Integration Example
- Checking Access
- React Component UI
- Reading & Writing Rules (PHP)
- REST API
- Events
- Custom Providers
- Built-in Providers
- Important Notes
- Database Table Reference
Requirements
| PHP | 7.4+ |
| WordPress | 5.9+ |
| Node.js | 18+ (only needed if you rebuild the JS assets) |
automattic/jetpack-autoloader |
^5.0 (mandatory — see below) |
berlindb/core |
^2.0 (DB layer) |
Installation
Your composer.json must include Jetpack Autoloader:
Why Jetpack Autoloader is mandatory
If two plugins install this library at different versions, PHP throws a fatal "class already declared" error. Jetpack Autoloader scans every installed plugin, finds all copies, and loads only the newest one.
In your plugin's main file, require the Jetpack Autoloader entry point —
not the standard vendor/autoload.php:
PHP Setup
1. Boot the manager
Declare $manager at file scope (outside any closure) so every subsequent
hook can capture it via use. Always pass a plugin-specific filter tag to
prevent your providers bleeding into other plugins that also use this library.
AccessControlManager owns a RuleQuery internally. Instantiating it
registers RuleTable via BerlinDB, which creates or upgrades the
{prefix}wpb_access_control table automatically on admin_init.
Need to wait for other plugins first? Use a reference capture instead:
2. Register the REST API
Call register_rest_api() from rest_api_init to expose the wpb-ac/v1
endpoints. The consuming plugin decides whether to enable them.
Complete Integration Example
Below is a self-contained my-plugin.php showing all pieces wired together:
initialising the manager, registering the REST API, enqueueing the React UI,
rendering the mount point, and checking access.
Key points:
$manageris declared once at file scope; all hooks capture it withuse ( $manager ).add_submenu_page()(oradd_menu_page()) returns the hook suffix — store it and compare inadmin_enqueue_scriptsto load assets only on your page.vendor/autoload_packages.phpis the Jetpack Autoloader entry point, not the standardvendor/autoload.php.
Checking Access
Access hierarchy
| Step | Condition | Result |
|---|---|---|
| 1 | access_control_key is empty or 'everyone' |
Allow |
| 2 | User has manage_options (administrator) |
Always allow |
| 3 | User ID = 0 (unauthenticated) | Deny |
| 4 | No provider registered for the configured key | Deny |
| 5 | provider->user_has_access() |
Allow or Deny |
React Component UI
The library ships a pre-built React component that renders a complete
Access Control settings panel. Drop it into any WordPress admin page and it
wires itself to the wpb-ac/v1 REST API automatically.
What it looks like
The component has four states driven by a single "Who can access" dropdown:
| Dropdown option | Extra UI |
|---|---|
| No user access added by admin | Nothing — resource is locked (except admins) |
| Everyone (no restriction) | Nothing — all users can access |
| WordPress Role | Checkboxes for each WordPress role |
| Users | Search-as-you-type field + selected-user tags |
Custom providers registered via the filter also appear in the dropdown. If
they expose options, checkboxes are rendered automatically.
Enqueue the built assets
The compiled assets live in assets/build/. The .asset.php file declares
all required WordPress script dependencies so you never need to list them manually.
Getting the right hook suffix:
add_menu_page()andadd_submenu_page()both return a hook suffix string (e.g."settings_page_my-plugin"). Capture that return value and compare it inadmin_enqueue_scriptsso assets load only on your page.
Render target
Add an empty <div> with the id wpb-access-control anywhere in your admin
page template. The component mounts itself automatically.
Component props reference
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
namespace |
string |
✅ | — | Access-control namespace, e.g. "mcp" |
resourceKey |
string |
✅ | — | Resource key within the namespace |
restApiRoot |
string |
✅ | — | WP REST API root URL (get_rest_url()) |
nonce |
string |
✅ | — | wp_create_nonce('wp_rest') |
title |
string |
"Access Control" |
Card heading | |
description |
string |
(MCP-server copy) | Subtitle paragraph | |
saveLabel |
string |
"Save Access Control" |
Save button label | |
onSave |
Function |
— | Callback (acKey, acOptions) after a successful save |
Using the component as a JS import
If your plugin has its own webpack build, import the component directly:
Note: When importing directly the nonce middleware must be registered before the first
apiFetchcall. The auto-render path (index.js) handles this automatically.
Namespace slashes
Namespaces containing slashes (e.g. procureco/v1) are handled automatically
by the component — each segment is encodeURIComponent-encoded so they reach
the REST API as %2F.
Reading & Writing Rules (PHP)
Use RuleQuery when you need to read or write rules from PHP directly.
You can also access the same instance through the manager:
REST API
REST namespace: wpb-ac/v1
All endpoints require manage_options (administrator) by default.
Use the wpb_access_control_rest_permission filter to override.
Endpoints
| Method | Path | Description |
|---|---|---|
GET |
/rules/{namespace}/{key} |
Read the current rule |
PUT |
/rules/{namespace}/{key} |
Create or replace a rule |
DELETE |
/rules/{namespace}/{key} |
Clear a rule (revert to unrestricted) |
DELETE |
/namespaces/{namespace} |
Purge all rules for a namespace |
GET |
/providers |
List registered providers and their options |
GET |
/users?search=...&limit=10 |
Search WordPress users |
Slashes in namespace: The
{namespace}URL segment cannot contain literal slashes — encode them as%2F:.../rules/procureco%2Fv1/my-key. The{key}segment allows literal slashes.
Request / response shapes
GET /rules/{namespace}/{key}
PUT /rules/{namespace}/{key} — body:
Response:
DELETE /rules/{namespace}/{key}
DELETE /namespaces/{namespace}
GET /providers
GET /users?search=jane&limit=10
Authentication
WordPress admin (nonce)
Include the wp_rest nonce in the X-WP-Nonce header:
Application Passwords (external clients)
Code examples
cURL
PHP (wp_remote_request)
@wordpress/api-fetch
Vanilla fetch
Permission filter
Override who may call any endpoint:
Write authorization filter
Restrict which namespace/key pairs may be modified:
Events
wpb_access_control_denied
Fires whenever user_has_access() returns false (steps 3–5 of the hierarchy).
wpb_access_control_saved
Fires after any successful write via the REST API (PUT rule, DELETE rule,
DELETE namespace). $ac_key is '' on a clear.
Custom Providers
Register
The filter tag must match the string passed to AccessControlManager.
Register at init priority ≤ 4 (the filter fires at priority 5).
Contract (AbstractProvider)
| Method | Required | Description |
|---|---|---|
get_id(): string |
✅ | Unique slug stored as access_control_key |
get_label(): string |
✅ | Human-readable label shown in the UI dropdown |
get_options(): array |
✅ | [['id'=>'slug','label'=>'Name'], ...]; return [] for dynamic providers |
user_has_access(int $user_id, array $selected): bool |
✅ | Core access check |
is_available(): bool |
Return false when a required dependency is inactive |
Example provider
Providers and their options are surfaced by GET /wpb-ac/v1/providers, so
any front-end UI (including the built-in React component) can render the
correct controls dynamically without hard-coding provider IDs.
Built-in Providers
| Provider ID | Class | Description |
|---|---|---|
wp_role |
WpRoleProvider |
Restricts by WordPress user role. Administrator is always bypassed. |
wp_user |
WpUserProvider |
Restricts to specific WordPress users by ID. |
WpRoleProvider filters
| Filter | Signature | Description |
|---|---|---|
wpb_access_control_wp_role_options |
(array $options): array |
Add or remove selectable role options |
wpb_access_control_wp_role_has_access |
(bool $result, int $user_id, array $selected): bool |
Override the final role-based decision |
WpUserProvider
Options are user IDs stored as strings ("42"), not usernames or emails —
sanitize_key() strips @ and ., so email addresses would be corrupted.
| Filter | Signature | Description |
|---|---|---|
wpb_access_control_wp_user_has_access |
(bool $result, int $user_id, array $selected): bool |
Override the final per-user decision |
Important Notes
Filter tag isolation
Always pass a plugin-specific tag to AccessControlManager. Two plugins
sharing the same filter tag will bleed providers into each other's checks.
Table management
BerlinDB handles all table creation and upgrades automatically on admin_init.
No activation hook is needed — instantiating new AccessControlManager(...) is sufficient.
Caching
Always use RuleQuery::set_rule() and clear_rule(). Direct $wpdb writes
bypass BerlinDB's object cache and leave it stale.
Administrator bypass is unconditional
Any user with manage_options always passes user_has_access() regardless of
the stored rule. This cannot be disabled.
Uninstall cleanup
Each consuming plugin removes its own rows:
Multisite
The table uses $wpdb->prefix — each sub-site has its own
{prefix}wpb_access_control table. Network-wide rules must be handled by
the consuming plugin.
Database Table Reference
Table: {prefix}wpb_access_control · DB layer: BerlinDB ^2.0 · Schema version: 202605120001
| Column | Type | Notes |
|---|---|---|
id |
BIGINT UNSIGNED PK AI | |
namespace |
VARCHAR(100) NOT NULL | Plugin-scoped prefix, e.g. mcp, procureco/v1 |
key |
VARCHAR(255) NOT NULL | Resource identifier within the namespace |
access_control_key |
VARCHAR(100) NOT NULL | Rule type slug — same for every row of a (ns, key) pair |
access_control_value |
VARCHAR(255) NOT NULL | One option per row; '' for the everyone sentinel |
created_at |
DATETIME | BerlinDB-managed on INSERT |
updated_at |
DATETIME | BerlinDB-managed on UPDATE |
Indexes: PRIMARY KEY (id) · UNIQUE (namespace, key(191), access_control_value) · KEY (namespace, key(191))
Rule storage convention
| Logical state | Rows in table |
|---|---|
| No rule configured | No rows for that (namespace, key) |
everyone |
One row: access_control_key='everyone', access_control_value='' |
wp_role + ['editor','author'] |
Two rows, both access_control_key='wp_role'; values 'editor', 'author' |
wp_user + ['1','42'] |
Two rows, both access_control_key='wp_user'; values '1', '42' |