zendframework / maintainers

Contributors and maintainers information for Zend Framework.
BSD 3-Clause "New" or "Revised" License
50 stars 25 forks source link

[RFC] Authorization middleware for Expressive and PSR-7 apps #19

Closed ezimuel closed 7 years ago

ezimuel commented 7 years ago

Goal

The goal of this RFC is to propose a user authorization middleware for Expressive and PSR-7 applications.

The authorization module will be used to authorize authenticated user based on URL route and HTTP methods.

Requirements

The authorization module will require an authentication module to create a user instance to be passed as PSR-7 attribute. This user attribute will be the input of the authorization middleware.

We will require a MiddlewareAuthentication::class attribute, as follows:

$user = $request->getAttribute(AuthenticationMiddleware::class, false);
if (false === $user) {
    return new Zend\Diactoros\Response\EmptyResponse(401);
}

If the user will be not available, the authorization module will returns a 401 Unauthorized response.

Moreover, for the purpose of the authorization module, we will need the user's role. We can get the role using a $user->getRole() function, a $user->role property or a $user['role'] value. The choose of which method to use is left to the user implementation.

Implementation

The idea is to build an Expressive module that offers user authorization using a Role-based access control (RBAC). We will use the zend-permissions-rbac library for implementing the RBAC system. Using the RBAC terminology, a role is the user's role and a resource is the route name. In this way we can authorize URLs and HTTP methods for a specific user's role.

This approach is the same used in this blog post. The authorization can be configured using a PHP file like to the follows:

return [
    'roles' => [
        'administrator' => [],
        'editor'        => ['administrator'],
        'contributor'   => ['editor'],
    ],
    'permissions' => [
        'contributor' => [
            'admin.dashboard',
            'admin.posts',
        ],
        'editor' => [
            'admin.publish',
        ],
        'administrator' => [
            'admin.settings',
        ],
    ],
];

where the roles can be configured using a parent role (e.g. administrator is parent of editor). The permissions are configured using route names (e.g. contributor is allowed to access the routes admin.dashboard and admin.posts).

This authorization middleware will be consumed during the pipe of a route. Here an example:

$app->get('/admin/dashboard', [
    AuthenticationMiddleware::class,
    AuthorizationMiddleware::class,
    Action\DashboardAction::class
], 'admin.dashboard');

Before the authorization, we assumed the usage of an authentication module (AuthenticationMiddleware). The AuthenticationMiddleware will store a user object as PSR-7 attribute.

Dynamic assertion

Using the route names as resources address the goal to authorize user's role based on URLs and HTTP methods. There are some use cases where this is not enough. For instance, in a blog application we may need to authorize a user if he/she is the onwer of an article.

This scenario can be implemented using the dynamic assertion feature of zend-permissions-rbac. Implementing the Zend\Permissions\Rbac\AssertionInterface interface we can customize the authorization adding more constrains. For instance, we can limit the access of a route only for specific user's ID as the previous blog requirement.

Prototype

A prototype of the implementation will be available at ezimuel/zend-expressive-authorization.

weierophinney commented 7 years ago

Discussion has moved to the forums.