danilovl/permission-middleware-bundle

Symfony bundle provides simple mechanism control permission for class or his method.

v2.7.4 2024-10-28 10:22 UTC

README

phpunit downloads latest Stable Version license

PermissionMiddlewareBundle

About

Symfony bundle provides simple mechanism control permission for class or his method.

Requirements

  • PHP 8.3 or higher
  • Symfony 7.0 or higher

1. Installation

Install danilovl/permission-middleware-bundle package by Composer:

composer require danilovl/permission-middleware-bundle

Add the PermissionMiddlewareBundle to your application's bundles if does not add automatically:

<?php
// config/bundles.php

return [
    // ...
    Danilovl\PermissionMiddlewareBundle\PermissionMiddlewareBundle::class => ['all' => true]
];

2. Usage

Configuration tree options for attribute.

The accessDeniedHttpException parameter will be useful for ClassMiddleware, ServiceMiddleware when you create custom response and you won't want throw default AccessDeniedHttpException.

$configurationTree = [
    'user' => [
        'roles',
        'userNames',
        'exceptionMessage' => [
            'message',
            'messageParameters',
            'domain',
            'locale'
        ],
        'redirect' => [
            'route',
            'parameters',
            'flash' => [
                'type',
                'trans' => [
                    'message',
                    'messageParameters',
                    'domain',
                    'locale'
                ]
            ]
        ]
    ],
    'date' => [
        'from',
        'to',
        'exceptionMessage' => [
            'message',
            'messageParameters',
            'domain',
            'locale'
        ],
        'redirect' => [
            'route',
            'parameters',
            'flash' => [
                'type',
                'trans' => [
                    'message',
                    'messageParameters',
                    'domain',
                    'locale'
                ]
            ]
        ]
    ],
    'redirect' => [
        'route',
        'parameters',
        'flash' => [
            'type',
            'trans' => [
                'message',
                'messageParameters',
                'domain',
                'locale'
            ]
        ]
    ],
    'class' => [
        'name',
        'method',
        'exceptionMessage' => [
            'message',
            'messageParameters',
            'domain',
            'locale'
        ],
        'redirect' => [
            'route',
            'parameters',
            'flash' => [
                'type',
                'trans' => [
                    'message',
                    'messageParameters',
                    'domain',
                    'locale'
                ]
            ]
        ]
    ],   
    'service' => [
        'name',
        'method',
        'exceptionMessage' => [
            'message',
            'messageParameters',
            'domain',
            'locale'
        ],
        'redirect' => [
            'route',
            'parameters',
            'flash' => [
                'type',
                'trans' => [
                    'message',
                    'messageParameters',
                    'domain',
                    'locale'
                ]
            ]
        ]
    ],
    'environment',
    'afterResponse'
];

You can use PermissionMiddleware attribute for class or method. Method of ClassMiddleware, ServiceMiddleware accept Symfony\Component\HttpKernel\Event\ControllerEvent as argument and must return boolean.

<?php declare(strict_types=1);

namespace App\Controller;

use Danilovl\PermissionMiddlewareBundle\Attribute\PermissionMiddleware;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\{
    Request,
    Response
};

#[PermissionMiddleware(
    user: [
        'roles' => ['ROLE_SUPERVISOR'],
        'userNames' => ['admin'],
        'exceptionMessage' => [
            'message' => 'app.permission_method_user_error_message'    
        ],
        'redirect' => [
            'route' => 'profile_show',
            'flash' => [
                'type' => 'error',
                'trans' => [
                    'message' => 'app.permission_action_flash_message'
                ]
            ]
        ]
    ],
    date: [
        'from' => '01-01-2021',
        'exceptionMessage' => [
            'message' => 'app.permission_date_error_message'
        ],
        'redirect' => [
            'route' => 'profile_show',
            'flash' => [
                'type' => 'error',
                'trans' => [
                    'message' => 'app.permission_action_flash_message'
                ]
            ]
        ]
    ],
    redirect: [
        'route' => 'profile_show',
        'flash' => [
            'type' => 'warning',
            'trans' => [
                'message' => 'app.permission_action_flash_message'
            ]
        ]
    ],
    class: [
        'name' => 'App\Middleware\HomeControllerMiddleware',
        'method' => 'handle',
        'exceptionMessage' => [
            'message' => 'app.permission_date_error_message'
        ],
        'redirect' => [
            'route' => 'profile_show',
            'flash' => [
                'type' => 'error',
                'trans' => [
                    'message' => 'app.permission_action_flash_message'
                ]
            ]
        ]
    ],   
    service: [
       'name' => 'app.middleware.home_controller',
       'method' => 'handle',
    ]
)]
class HomeController extends AbstractController
{
    #[PermissionMiddleware(
        user: [
            'roles' => ['ROLE_SUPERVISOR'],
            'userNames' => ['admin'],
            'exceptionMessage' => [
                'message' => 'app.permission_method_user_error_message'    
            ],
            'redirect' => [
                'route' => 'profile_show',
                'flash' => [
                    'type' => 'error',
                    'trans' => [
                        'message' => 'app.permission_action_flash_message'
                    ]
                ]
            ]
        ],
        date: [
            'from' => '01-01-2021',
            'exceptionMessage' => [
                'message' => 'app.permission_date_error_message'
            ],
            'redirect' => [
                'route' => 'profile_show',
                'flash' => [
                    'type' => 'error',
                    'trans' => [
                        'message' => 'app.permission_action_flash_message'
                    ]
                ]
            ]
        ],
        redirect: [
            'route' => 'profile_show',
            'flash' => [
                'type' => 'warning',
                'trans' => [
                    'message' => 'app.permission_action_flash_message'
                ]
            ]
        ]
    )]
    public function index(Request $request): Response
     {
         return $this->render('home/index.html.twig');
     } 
     
    #[PermissionMiddleware(
        date: [
            'to' => '31-12-2020',
            'redirect' => [
                'route' => 'new_news',
                'flash' => [
                    'type' => 'warning',
                    'trans' => [
                        'message' => 'app.old_section_is_closed'
                    ]
                ]
            ]
        ]
    )]
    public function oldNews(Request $request): Response
    {
        return $this->render('home/news.html.twig');
    }
 
    #[PermissionMiddleware(
        date: [
            'from' => '01-01-2021',
            'redirect' => [
                'route' => 'new_news',
                'flash' => [
                    'type' => 'warning',
                    'trans' => [
                        'message' => 'app.new_section_is_open',
                        'parameters' => ['date' => '01-01-2021'],
                        'domain' => 'flashes',
                        'locale' => 'en'
                    ]
                ]
            ]
        ]
    )]
    public function news(Request $request): Response
    {
        return $this->render('home/news.html.twig');
    }

     #[PermissionMiddleware(
        user: [
            'roles' => ['ROLE_SUPERVISOR'],
            'userNames' => ['admin'],
            'redirect' => [
                'route' => 'homepage',
                'flash' => [
                    'type' => 'error',
                    'trans' => [
                        'message' => 'app.permission_denied'
                    ]
                ]
            ]
        ]
    )]
    public function editNews(Request $request): Response
    {
        return $this->render('home/edit_news.html.twig');
    }

   #[PermissionMiddleware(
        redirect: [
            'route' => 'homepage'
        ]
   )]
   public function redirect(Request $request): Response
   {
       return $this->render('home/redirect.html.twig');
   }

   #[PermissionMiddleware(
       redirect: [
           'route' => 'homepage',
           'flash' => [
               'type' => 'success',
               'trans' => [
                   'message' => 'app.redirect_success'
               ]
           ]
       ]
   )]
   public function redirectWithFlash(Request $request): Response
   {
       return $this->render('home/redirect.html.twig');
   }

   #[PermissionMiddleware(
       user: [
           'roles' => ['ROLE_ADMIN'],
           'userNames' => ['admin'],
           'redirect' => ['route' => 'login']
       ]
   )]
   public function admin(Request $request): Response
   {
       return $this->render('home/admin.html.twig');
   }

   #[PermissionMiddleware(
       user: [
           'userNames' => ['admin', 'editor', 'publisher'],
           'redirect' => [
               'route' => 'login'
           ]
       ]
   )]
   public function adminByUsernameRedirect(Request $request): Response
   {
       return $this->render('home/admin.html.twig');
   }
   
   #[PermissionMiddleware(
        user: [
            'userNames' => ['admin', 'editor', 'publisher'],
            'exceptionMessage' => [
                'message' => 'app.permission_denied'    
            ]
        ]
   )] 
   public function adminByUsernameExceptionMessage(Request $request): Response
   {
       return $this->render('home/admin.html.twig');
   }   
   
   #[PermissionMiddleware(
        class: [
            'name' => 'App\Middleware\ShowCalendarMiddleware',
            'method' => 'handle'
        ]
   )] 
   public function showCalendar(Request $request): Response
   {
       return $this->render('home/admin.html.twig');
   }   
   
   #[PermissionMiddleware(
       service: [
           'name' => 'app.middleware.create_article',
           'method' => 'handle'
       ]
   )] 
   public function createArticle(Request $request): Response
   {
       return $this->render('home/admin.html.twig');
   }   

   #[PermissionMiddleware(
       service: [
           'name' => 'app.middleware.create_article',
           'method' => 'handle'
       ],
       environment: ['dev']
   )] 
   public function createArticleOnyForDev(Request $request): Response
   {
       return $this->render('home/admin.html.twig');
   }
   
   #[PermissionMiddleware(
       service: [
           'name' => 'app.middleware.create_article',
           'method' => 'handle'
       ],
       environment: ['dev'],
       afterResponse: true
   )] 
   public function createArticleResponse(Request $request): Response
   {
       return $this->render('home/admin.html.twig');
   }
}

3. Example service, class handlers

Simple basic handler.

<?php declare(strict_types=1);

namespace App\Application\Controller;

class MiddlewareController
{
    public function __invoke(): bool
    {
        return true;
    }
}

Simple handler with ControllerEvent parameter. You can change response.

<?php declare(strict_types=1);

namespace App\Application\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ControllerEvent;

class MiddlewareController
{
    public function __invoke(ControllerEvent $event): bool
    {
        $request = $event->getRequest();
        if ($request->isXmlHttpRequest()) {
            return false;
        }

       $event->setController(static fn(): Response => new Response('Unauthorized', 401));

        return true;
    }
}

Return JsonResponse.

<?php declare(strict_types=1);

namespace App\Application\Controller;

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\ControllerEvent;

class MiddlewareController
{
    public function __invoke(ControllerEvent $event): bool
    {
        $request = $event->getRequest();
        if ($request->isXmlHttpRequest()) {
            return false;
        }

        $event->setController(static fn(): JsonResponse => new JsonResponse('Unauthorized', 401));

        return true;
    }
}

Return RedirectResponse.

<?php declare(strict_types=1);

namespace App\Application\Controller;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\ControllerEvent;

class MiddlewareController
{
    public function __invoke(ControllerEvent $event): bool
    {
        $request = $event->getRequest();
        if ($request->isXmlHttpRequest()) {
            return false;
        }

        $event->setController(static fn(): RedirectResponse => new RedirectResponse('https://www.google.com/', 302));

        return true;
    }
}

License

The PermissionMiddlewareBundle is open-sourced software licensed under the MIT license.