Robin Hermans
Robin Hermans

Reputation: 1599

Symfony2 get to the access_control parameters located in the security.yml

I'm trying to get the access_control parameters which are located in my security.yml as an array in my custom service.

Just like with getting the role_hierarchy parameters I thought it would work with the following code:

$accessParameters = $this->container->getParameter('security.access_control');

Unfortunately this was not the case. Can someone tell how to get the parameters?

Upvotes: 5

Views: 3681

Answers (2)

Kafoso
Kafoso

Reputation: 554

Necro, but still relevant. This is an improvement on Touki's answer above, where we don't reparse the access_control definitions, but rather use the already configured security token, firewall and access map to work out the answer.

.../services.yml

...

My\Application\AuthenticationBundle\Security\AccessControlHelper:
    class: My\Application\AuthenticationBundle\Security\AccessControlHelper
    arguments:
        $securityContext: "@security.context"
        $firewall: '@security.firewall.map'
        $accessDecisionManager: '@security.access.decision_manager'
        $accessMap: '@security.access_map'

...

src/My/Application/AuthenticationBundle/Security/AccessControlHelper.php

declare(strict_types=1);

namespace My\Application\AuthenticationBundle\Security;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Http\AccessMapInterface;
use Symfony\Component\Security\Http\Firewall\AccessListener;
use Symfony\Component\Security\Http\FirewallMapInterface;

class AccessControlHelper
{
    /**
     * @var SecurityContextInterface
     */
    protected $securityContext;

    /**
     * @var FirewallMapInterface
     */
    protected $firewallMap;

    /**
     * @var AccessDecisionManagerInterface
     */
    protected $accessDecisionManager;

    /**
     * @var AccessMapInterface
     */
    protected $accessMap;

    public function __construct(
        SecurityContextInterface $securityContext,
        FirewallMapInterface $firewallMap,
        AccessDecisionManagerInterface $accessDecisionManager,
        AccessMapInterface $accessMap
    )
    {
        $this->securityContext = $securityContext;
        $this->firewallMap = $firewallMap;
        $this->accessDecisionManager = $accessDecisionManager;
        $this->accessMap = $accessMap;
    }

    public function isRequestAccessible(Request $request): bool
    {
        $token = $this->securityContext->getToken();
        if (!$token || false == $token->isAuthenticated()) {
            return false;
        }

        list($listeners) = $this->firewallMap->getListeners($request);
        if ($listeners) {
            foreach ($listeners as $listener) {
                if ($listener instanceof AccessListener) {
                    /**
                     * Logic here is much inspired by the AccessListener->handle(...) method.
                     */
                    list($attributes) = $this->accessMap->getPatterns($request);

                    if (null === $attributes) {
                        continue;
                    }

                    return boolval($this->accessDecisionManager->decide($token, $attributes, $request));
                }
            }
        }
        return true;
    }

    public function isUriAccessible(string $uri)
    {
        return $this->isRequestAccessible(Request::create($uri));
    }
}

Sample usage:

use My\Application\AuthenticationBundle\Security\AccessControlHelper;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;

$container = ...; // @var ContainerInterface

$accessControlHelper = $container->get(AccessControlHelper::class);
$accessControlHelper->isRequestAccessible(new Request("/foo"));
$accessControlHelper->isUriAccessible("/foo");

Upvotes: 0

Touki
Touki

Reputation: 7525

There's no way to get the access_control parameter from the container.
This is because this parameter is only used to create request matchers which will be registered as AccessMap later given in the AccessListener, and then are left over without registering it into the container.

You can try something hacky to get these matchers back by getting them like

$context  = $this->get("security.firewall.map.context.main")->getContext();
$listener = $context[0][5];
// Do reflection on "map" private member

But this is kind of an ugly solution.

Another way I can see on how to get them is to parse again the security file

use Symfony\Component\Yaml\Yaml;

$file   = sprintf("%s/config/security.yml", $this->container->getParameter('kernel.root_dir'));
$parsed = Yaml::parse(file_get_contents($file));

$access = $parsed['security']['access_control'];

If you want to register this configuration into a service, you can do something like

services.yml

services:
    acme.config_provider:
        class: Acme\FooBundle\ConfigProvider
        arguments:
            - "%kernel.root_dir%"
    acme.my_service:
        class: Acme\FooBundle\MyService
        arguments:
            - "@acme.config_provider"

Acme\FooBundle\ConfigProvider

use Symfony\Component\Yaml\Yaml;

class ConfigProvider
{
    protected $rootDir;

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

    public function getConfiguration()
    {
        $file = sprintf(
            "%s/config/security.yml",
            $this->rootDir
        );
        $parsed = Yaml::parse(file_get_contents($file));

        return $parsed['security']['access_control'];
    }
}

Acme\FooBundle\MyService

class MyService
{
    protected $provider;

    public function __construct(ConfigProvider $provider)
    {
        $this->provider = $provider;
    }

    public function doAction()
    {
        $access = $this->provider->getConfiguration();

        foreach ($access as $line) {
            // ...
        }
    }
}

Upvotes: 5

Related Questions