Siren

Roles & Permissions

How current_user_can maps to Siren's action-based permission system and auth middleware.

Last updated: April 9, 2026

Roles & Permissions

In WordPress, you check permissions with current_user_can('capability_name'). Siren uses an action-based permission system where you check whether a user can perform a specific action (Create, Read, Update, Delete) on a specific model class.

// WordPress: string-based capability checks
if (current_user_can('manage_options')) {
    // User is an admin
}

if (current_user_can('edit_post', $post_id)) {
    // User can edit this specific post
}
// Siren outside a REST request: resolve the current user and check actions
use PHPNomad\Auth\Interfaces\CurrentUserResolverStrategy;
use PHPNomad\Auth\Models\Action;
use Siren\Programs\Core\Models\Program;
use Siren\Auth\Core\Enums\ActionTypes;

class MyService
{
    protected CurrentUserResolverStrategy $userResolver;

    public function __construct(CurrentUserResolverStrategy $userResolver)
    {
        $this->userResolver = $userResolver;
    }

    public function checkAccess(): void
    {
        $user = $this->userResolver->getCurrentUser();

        if ($user->canDoAction(new Action(ActionTypes::Update, Program::class))) {
            // User can update programs
        }

        if ($user->canDoAction(new Action(ActionTypes::Read, Program::class))) {
            // User can read programs
        }
    }
}
// Siren inside a REST controller: user is on the request, middleware handles auth

use PHPNomad\Auth\Enums\ActionTypes;
use PHPNomad\Auth\Models\Action;
use PHPNomad\Rest\Exceptions\AuthorizationException;
use PHPNomad\Rest\Interfaces\Controller;
use PHPNomad\Rest\Interfaces\HasMiddleware;
use PHPNomad\Rest\Interfaces\Request;
use PHPNomad\Rest\Interfaces\Response;
use Siren\Programs\Core\Models\Program;

class UpdateProgramController implements Controller, HasMiddleware
{
    public function getMiddleware(): array
    {
        return [
            // Middleware checks auth before handle() runs
            // Returns 401 if not authenticated, 403 if not authorized
            createAuthMiddlewareFromCurrentContext(),
        ];
    }

    public function handle(Request $request): Response
    {
        // If you reach here, the user is authenticated and authorized
        $user = $request->getUser();

        // Additional checks if needed
        if (!$user->canDoAction(new Action(ActionTypes::Update, Program::class))) {
            throw new AuthorizationException('Cannot update programs');
        }

        // ... handle the request
    }
}

How does the action system work?

Siren’s permission model is built on two concepts: action types and model classes.

ActionTypes is an enum with four values:

ActionTypeEquivalent WordPress capability pattern
ActionTypes::Createcreate_posts, create_users
ActionTypes::Readread_post, list_users
ActionTypes::Updateedit_post, edit_users
ActionTypes::Deletedelete_post, delete_users

The model class specifies what the action applies to. Instead of WordPress’s string-based capability names like 'edit_post', Siren uses new Action(ActionTypes::Update, Program::class). This is type-safe. Your IDE catches typos and you can refactor model class names without breaking permission checks.

Under the hood, Siren maps these action/model pairs to WordPress capability strings. An action like new Action(ActionTypes::Read, Program::class) resolves to a capability string like siren_action__read__Siren\Programs\Core\Models\Program. WordPress’s role system stores and checks these capabilities, so Siren’s permission model layers on top of WordPress rather than replacing it.

How do REST controllers handle auth?

Inside REST controllers, you typically do not check permissions manually. The createAuthMiddlewareFromCurrentContext() middleware handles authentication and authorization before your handle() method runs:

  1. Authentication verifies the user has a valid auth token. Returns 401 if not.
  2. Authorization checks that the authenticated user has the required capabilities for the controller’s action. Returns 403 if not.

By the time your handler executes, the user is guaranteed to be authenticated and authorized. Use $request->getUser() to access the authenticated user object for any additional logic.

When do I need manual permission checks?

Manual canDoAction() checks are useful in two cases:

For conditional logic within a handler, the middleware grants access to the endpoint, but you may need finer-grained checks inside:

public function handle(Request $request): Response
{
    $user = $request->getUser();

    // The user can access this endpoint, but can they update THIS program?
    if (!$user->canDoAction(new Action(ActionTypes::Update, Program::class))) {
        // Show read-only view instead
    }
}

For code outside REST controllers, listeners, services, and admin hooks do not have middleware. Use the CurrentUserResolverStrategy to get the current user and check permissions directly.

Where can I learn more?

For the full list of action types, model classes, and the generated capability strings, see Enums & Constants.

For the full PHPNomad framework documentation on auth, see Auth.