Sonata Admin - Only allow show what logged in user has created

user1697652 picture user1697652 · Oct 11, 2012 · Viewed 9k times · Source

I've setup a sonata admin interface which allows users to create specific content, but how do I restrict users from editing content created by other users?

For arguments sake, a user logs in and creates a blog. In the list view of blogs, only the blogs that user created should be displayed.

Currently, everything is displayed to every user - I do have groups/roles setup to restrict access to admin areas, which works fine.

The only way I can currently think of only show a specific logged in users content, is to override the templates?

But, surely, this is an obvious and simple configuration setting?

Answer

pulzarraider picture pulzarraider · Oct 16, 2012

There are more options how to display only data of currently logged user:

The first prefered way is to use Symfony's ACL, here are more informations, how it works and how to do it in Sonata.

If you don't want to use ACL, you can change the default queries by adding some condition to SQL query (DQL query), for example ' WHERE adminId=5'. This will be more work, but I will show you basic example, how to do it.

Change your definition of your admin services in services.xml by adding new setter setSecurityContext. I will use admin for listing and editing some products.

    <service id="acme_demo_admin.product" class="Acme\Bundle\DemoAdminBundle\Admin\ProductAdmin">
        <tag name="sonata.admin" manager_type="orm" group="product_group" label_catalogue="admin" label="Products"/>
        <argument />
        <argument>Acme\Bundle\DemoAdminBundle\Entity\Product</argument>
        <argument>AcmeDemoAdminBundle:ProductAdmin</argument>

        <call method="setSecurityContext">
            <argument type="service" id="security.context" />
        </call>
    </service>

SecurityContext is service, that contains informations about currently logged user.

In Acme/Bundle/DemoAdminBundle/Admin/ProductAdmin.php add setter setSecurityContext and change createQuery method:

<?php

namespace Acme\Bundle\DemoAdminBundle\Admin;

use Symfony\Component\Security\Core\SecurityContextInterface;
// ...

class ProductAdmin extends Admin
{
    /**
     * Security Context
     * @var \Symfony\Component\Security\Core\SecurityContextInterface
     */
    protected $securityContext;

    public function setSecurityContext(SecurityContextInterface $securityContext)
    {
        $this->securityContext = $securityContext;
    }

    protected function configureRoutes(RouteCollection $collection)
    {
        //remove all routes except those, you are using in admin and you can secure by yourself
        $collection
                ->clearExcept(array(
                    'list',
                    'edit',
                ))
        ;
    }

    public function createQuery($context = 'list')
    {
        $queryBuilder = $this->getModelManager()->getEntityManager($this->getClass())->createQueryBuilder();

        //if is logged admin, show all data
        if ($this->securityContext->isGranted('ROLE_ADMIN')) {
            $queryBuilder->select('p')
                    ->from($this->getClass(), 'p')
             ;
        } else {
            //for other users, show only data, which belongs to them
            $adminId = $this->securityContext->getToken()->getUser()->getAdminId();

            $queryBuilder->select('p')
                    ->from($this->getClass(), 'p')
                    ->where('p.adminId=:adminId')
                    ->setParameter('adminId', $adminId, Type::INTEGER)
            ;
        }

        $proxyQuery = new ProxyQuery($queryBuilder);
        return $proxyQuery;
    }

    //... configureListFields, configureDatagridFilters etc.
}

Users without role SONATA_ADMIN can't see all records.

Second step - secure some special routes, for exmaple edit - you should check if currently logged admin can edit the specified product.

You can create your own security voter (prefered solution) or use custom CRUD Controller.

In custom CRUD Controller: Acme/Bundle/DemoAdminBundle/Controller/ProductController.php overload editAction.

<?php
    namespace Acme\Bundle\DemoAdminBundle\Controller;

    use Sonata\AdminBundle\Controller\CRUDController as Controller;
    use Symfony\Component\Security\Core\Exception\AccessDeniedException;


    class ProductAdminController extends Controller 
    {
        public function editAction($id = null)
        {
            $request = $this->getRequest();
            $id = $request->get($this->admin->getIdParameter());

            $securityContext = $this->get('security.context');
            if (!$securityContext->isGranted('ROLE_ADMIN')) {

               $adminId = $securityContext->getToken()->getUser()->getId();

               $accessGranted = //here you should check if user with adminId can edit product with $id

                if (!$accessGranted) {
                    throw new AccessDeniedException(sprintf('Admin ID %s has no access to product with id %s', $adminId, $id));
                }
            }

            return parent::editAction($id);
        }

    }

As you can see, you can overload many methods and routes to add any functionality, you need. But as I said before, this is more work, so instead of overloading methods, first check if Symfony`s ACL (or just creating own security voter) is what you need in your project or not.