The Role-Based Access Control (RBAC) component provides role-based authorization abstraction for the Devflow CMF.
Introduction
Role-Based Access Control (RBAC) is based on the idea of roles rather than permissions as you may find in ACL.
In a web application, users will typically have identities defined by username, email, token, etc.
RBAC System:
- An Identity has one or more roles
- A role requests access to a permission
- A permission is given to a role
Thus, RBAC has:
- Many-to-many relationship between identities and roles.
- Many-to-many relationship between roles and permissions.
- Roles can have a parent role.
Rules
Sometimes you need to perform an extra check. For example, what if you only want authors to edit, update or delete
their own content, but not someone else's content? You can do that by setting a rule. You can do so by implementing
the AssertionRule interface with the execute() method.
<?php
declare(strict_types=1);
namespace Infrastructure\Service;
use Codefy\Framework\Auth\Rbac\Entity\AssertionRule;
final class AuthorRule implements AssertionRule
{
/**
* @param array|null $params
*
* @return bool
*/
public function execute(?array $params = null): bool
{
// @var Post $post
if($post = $params['post'] ?? null) {
return $post->authorId === ($params['userId'] ?? null);
}
return false;
}
}
File: ./Cms/Infrastructure/Service/AuthorRule.php
Configure RBAC
<?php
use Infrastructure\Service\AuthorRule;
return [
/** Named or grouped permissions. */
'permissions' => [
'admin' => [
'description' => 'All system permissions',
'permissions' => [
'access:admin' => ['description' => 'Access to the dashboard.'],
'create:content' => ['description' => 'Create content.'],
'create:product' => ['description' => 'Create product.'],
'create:users' => ['description' => 'Create users.'],
'manage:content' => ['description' => 'Manage content.'],
'manage:product' => ['description' => 'Manage product.'],
'manage:users' => ['description' => 'Manage users.'],
'manage:media' => ['description' => 'Manage media.'],
'manage:options' => ['description' => 'Manage options.'],
'manage:settings' => ['description' => 'Manage settings.'],
'manage:products' => ['description' => 'Manage product inventory.'],
'manage:plugins' => ['description' => 'Manage plugins.'],
'manage:themes' => ['description' => 'Manage themes.'],
'update:content' => ['description' => 'Update content.'],
'update:product' => ['description' => 'Update product.'],
'update:users' => ['description' => 'Update users.'],
'delete:content' => ['description' => 'Delete content.', 'ruleClass' => AuthorRule::class],
'delete:product' => ['description' => 'Delete product.'],
'delete:users' => ['description' => 'Delete users.'],
'manage:profile' => ['description' => 'User can manage their profile.'],
'switch:user' => ['description' => 'Switch user'],
'publish:content' => ['description' => 'Publish content.'],
'publish:product' => ['description' => 'Publish product.'],
'activate:plugins' => ['description' => 'Activate plugins.'],
'deactivate:plugins' => ['description' => 'Deactivate plugins.'],
],
],
'sites' => [
'description' => 'All site permissions',
'permissions' => [
'manage:sites' => ['description' => 'Manage sites.'],
'create:sites' => ['description' => 'Create sites.'],
'update:sites' => ['description' => 'Update sites.'],
'delete:sites' => ['description' => 'Delete sites.'],
]
],
'webmaster' => [
'description' => 'Website creator and builder.',
'permissions' => [
'vihzhuo:manage' => ['description' => 'Can use the Vihzhuo pagebuilder to build pages.'],
],
],
],
'roles' => [
'super' => [
'description' => 'Super administrator',
'permissions' => ['admin','sites','webmaster'],
],
'admin' => [
'description' => 'Administrator',
'permissions' => [
'access:admin',
'create:content',
'manage:content',
'update:content',
'delete:content',
'publish:content',
'create:product',
'manage:product',
'update:product',
'delete:product',
'publish:product',
'create:users',
'manage:users',
'update:users',
'delete:users',
'switch:user',
'manage:media',
'manage:options',
'manage:settings',
'manage:plugins',
'manage:themes',
'manage:profile',
'activate:plugins',
'deactivate:plugins',
'webmaster',
],
],
'editor' => [
'description' => 'Site editor',
'permissions' => [
'access:admin',
'create:content',
'manage:content',
'update:content',
'delete:content',
'publish:content',
'create:product',
'manage:product',
'update:product',
'delete:product',
'publish:product',
'manage:media',
'manage:profile',
'webmaster',
],
],
'user' => [],
],
];
File: ./config/rbac.php
Check Rights
<?php
use function App\Shared\Helpers\current_user_can;
if(current_user_can('delete:content', ['userId' => $userId, 'post' => $post]) {
... // The user is author of the post and can delete it
}
Authorizing Users
Devflow provides several middlewares for checking and validating user roles, permissions, and session handling.
If you need to check whether a user is authorized to view a certain page, you will need to add
Codefy\Framework\Http\Middleware\Auth\UserAuthorizationMiddleware or the Injector alias (user.authorization)
to your route:
<?php
declare(strict_types=1);
return (function(\Qubus\Routing\Psr7Router $router) {
$router->get('/protected/', function() {
//
})
->middleware('user.authorization')
->name('protected.page');
});
File: ./routes/web/web.php
When a user visits the /protected/ route, the middleware will check if the user is logged in. If the user is
logged in, the user will continue on, otherwise, the user will be redirected to your login route via the
redirect_guests_to setting in ./config/auth.php.
A different approach would be to check permissions via the controller. You can use the App\Shared\Helpers\current_user_can
helper or the Gate Middleware:
<?php
declare(strict_types=1);
namespace Application\Http\Controller;
use App\Application\Devflow;
use Psr\Http\Message\ResponseInterface;
use Qubus\Routing\Psr7Router;
use function App\Shared\Helpers\current_user_can;
use function Codefy\Framework\Helpers\trans;
use function Codefy\Framework\Helpers\view;
use function Qubus\Routing\Helpers\redirect;
final class ProtectedController
{
public function __construct(protected Psr7Router $router)
{
}
public function securePage(): ResponseInterface
{
if (false === current_user_can('manage:content')) {
Devflow::$PHP->flash->error(
message: 'Access denied.'
);
return redirect($this->router->url(name: 'protected.page'));
}
return view(
template: 'cmf::backend/protected',
data: ['title' => trans('Protected Page')]
);
}
}
File: ./Cms/Application/Http/Controller/ProtectedController.php
If you use the gate middleware, then your controller becomes cleaner by adding the gate middleware to your route:
<?php
declare(strict_types=1);
use Application\Http\Controller\ProtectedController;
return (function(\Qubus\Routing\Psr7Router $router) {
$router->get('/protected/', function(ProtectedController $controller) {
return $controller->securePage();
})
->middleware(['gate:manage:content,/admin/login/']);
});
File: ./routes/web/admin.php
<?php
declare(strict_types=1);
namespace Application\Http\Controller;
use Psr\Http\Message\ResponseInterface;
use function Codefy\Framework\Helpers\trans;
use function Codefy\Framework\Helpers\view;
final class ProtectedController
{
public function securePage(): ResponseInterface
{
return view(
template: 'cmf::backend/protected',
data: ['title' => trans('Protected Page')]
);
}
}
File: ./Cms/Application/Http/Controller/AdminController.php
current_user_can('manage:content') is what's used to check if a logged-in user
has a certain permission to continue.
Here is a list of other authentication middlewares along with their aliases:
Codefy\Framework\Http\Middleware\Auth\ExpireUserSessionMiddleware- Alias:
user.session.expire - Description: This middleware can be used for a logout route to clear the user's session and cookie.
- Alias:
Codefy\Framework\Http\Middleware\Auth\AuthenticationMiddleware- Alias:
user.authenticate - Description: This middleware can be used for a login route which checks the submitted login credentials against the database.
- Alias:
Codefy\Framework\Http\Middleware\Auth\UserSessionMiddleware- Alias:
user.session - Description: This middleware should be used with the previous middleware. If authentication is successful, the user session and cookie will be created.
- Alias: