PermissionVoter::supportsAttribute()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
/**
4
 * This file is part of tenside/core.
5
 *
6
 * (c) Christian Schiffler <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * This project is provided in good faith and hope to be usable by anyone.
12
 *
13
 * @package    tenside/core
14
 * @author     Christian Schiffler <[email protected]>
15
 * @copyright  2015 Christian Schiffler <[email protected]>
16
 * @license    https://github.com/tenside/core/blob/master/LICENSE MIT
17
 * @link       https://github.com/tenside/core
18
 * @filesource
19
 */
20
21
namespace Tenside\CoreBundle\Security;
22
23
use Symfony\Component\Config\ConfigCacheFactory;
24
use Symfony\Component\Config\ConfigCacheFactoryInterface;
25
use Symfony\Component\Config\ConfigCacheInterface;
26
use Symfony\Component\HttpFoundation\Request;
27
use Symfony\Component\HttpFoundation\RequestStack;
28
use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
29
use Symfony\Component\Routing\RouterInterface;
30
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
31
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
32
33
/**
34
 * This class checks the permissions of the authenticated user against the current request.
35
 */
36
class PermissionVoter implements VoterInterface, WarmableInterface
37
{
38
    /**
39
     * The router.
40
     *
41
     * @var RouterInterface
42
     */
43
    private $router;
44
45
    /**
46
     * The request stack.
47
     *
48
     * @var RequestStack
49
     */
50
    private $requestStack;
51
52
    /**
53
     * The options.
54
     *
55
     * @var array
56
     */
57
    protected $options = array();
58
59
    /**
60
     * The config cache.
61
     *
62
     * @var ConfigCacheFactoryInterface|null
63
     */
64
    private $configCacheFactory;
65
66
    /**
67
     * Create a new instance.
68
     *
69
     * @param RouterInterface $router       The router component.
70
     *
71
     * @param RequestStack    $requestStack The request stack.
72
     */
73
    public function __construct(RouterInterface $router, RequestStack $requestStack, $options)
74
    {
75
        $this->router       = $router;
76
        $this->requestStack = $requestStack;
77
        $this->options      = $options;
78
    }
79
80
    /**
81
     * {@inheritDoc}
82
     */
83
    public function supportsAttribute($attribute)
84
    {
85
        return 'ROLE_CHECK' === $attribute;
86
    }
87
88
    /**
89
     * {@inheritDoc}
90
     *
91
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
92
     */
93
    public function supportsClass($class)
0 ignored issues
show
Unused Code introduced by
The parameter $class is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
94
    {
95
        return true;
96
    }
97
98
    /**
99
     * {@inheritDoc}
100
     *
101
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
102
     */
103
    public function vote(TokenInterface $token, $object, array $attributes)
104
    {
105
        if (!(($object instanceof Request) || $this->supportsAnyAttribute($attributes))) {
106
            return VoterInterface::ACCESS_ABSTAIN;
107
        }
108
109
        $requiredRole = $this->getRequiredRole($object);
110
111
        if (null === $requiredRole) {
112
            return VoterInterface::ACCESS_ABSTAIN;
113
        }
114
115
        $user = $token->getUser();
116
117
        if (!$user instanceof UserInformationInterface) {
118
            return VoterInterface::ACCESS_DENIED;
119
        }
120
121
        foreach ($user->getRoles() as $role) {
122
            if (strtoupper($role) == strtoupper($requiredRole)) {
123
                return VoterInterface::ACCESS_GRANTED;
124
            }
125
        }
126
127
        return VoterInterface::ACCESS_DENIED;
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function warmUp($cacheDir)
134
    {
135
        $currentDir = $this->options['cache_dir'];
136
137
        // force cache generation
138
        $this->options['cache_dir'] = $cacheDir;
139
        $this->getRouteRoles();
140
141
        $this->options['cache_dir'] = $currentDir;
142
    }
143
144
    /**
145
     * Test if we support any of the attributes.
146
     *
147
     * @param string[] $attributes The attributes to test.
148
     *
149
     * @return bool
150
     */
151
    private function supportsAnyAttribute($attributes)
152
    {
153
        foreach ($attributes as $attribute) {
154
            if ($this->supportsAttribute($attribute)) {
155
                return true;
156
            }
157
        }
158
159
        return false;
160
    }
161
162
    /**
163
     * Provides the ConfigCache factory implementation, falling back to a default implementation if necessary.
164
     *
165
     * @return ConfigCacheFactoryInterface $configCacheFactory
166
     */
167
    private function getConfigCacheFactory()
168
    {
169
        if (null === $this->configCacheFactory) {
170
            $this->configCacheFactory = new ConfigCacheFactory($this->options['debug']);
171
        }
172
173
        return $this->configCacheFactory;
174
    }
175
176
    /**
177
     * Get the required roles from cache if possible.
178
     *
179
     * @return array
180
     */
181
    private function getRouteRoles()
182
    {
183
        $router = $this->router;
184
185
        $cache = $this->getConfigCacheFactory()->cache(
186
            $this->options['cache_dir'].'/tenside_roles.php',
187
            function (ConfigCacheInterface $cache) use ($router) {
188
                $routes = $router->getRouteCollection();
189
                $roles  = [];
190
                foreach ($routes as $name => $route) {
191
                    if ($requiredRole = $route->getOption('required_role')) {
192
                        $roles[$name] = $requiredRole;
193
                    }
194
                }
195
196
                $cache->write('<?php return ' . var_export($roles, true) . ';', $routes->getResources());
197
            }
198
        );
199
200
        return require_once $cache->getPath();
201
    }
202
203
    /**
204
     * Retrieve the required role for the current request (if any).
205
     *
206
     * @param mixed $object The object passed to the voter.
207
     *
208
     * @return string|null
209
     */
210
    private function getRequiredRole($object)
211
    {
212
        if (!(($request = $object) instanceof Request)) {
213
            $request = $this->requestStack->getCurrentRequest();
214
        }
215
216
        $routes = $this->getRouteRoles();
217
218
        if (isset($routes[$request->get('_route')])) {
219
            return $routes[$request->get('_route')];
220
        }
221
222
        return null;
223
    }
224
}
225