Passed
Pull Request — master (#101)
by Łukasz
03:05
created

SecuredManager::getElement()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 9
nc 3
nop 1
dl 0
loc 17
rs 9.4285
c 1
b 0
f 0
1
<?php
2
3
/**
4
 * (c) FSi sp. z o.o. <[email protected]>
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace FSi\Bundle\AdminSecurityBundle\Admin;
11
12
use FSi\Bundle\AdminBundle\Admin\Element;
13
use FSi\Bundle\AdminBundle\Admin\Manager\Visitor;
14
use FSi\Bundle\AdminBundle\Admin\ManagerInterface;
15
use FSi\Bundle\AdminSecurityBundle\Admin\SecuredElementInterface;
16
use FSi\Bundle\AdminSecurityBundle\Security\User\EnforceablePasswordChangeInterface;
17
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
18
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
19
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
20
21
class SecuredManager implements ManagerInterface
22
{
23
    /**
24
     * @var ManagerInterface
25
     */
26
    private $manager;
27
28
    /**
29
     * @var TokenStorageInterface
30
     */
31
    private $tokenStorage;
32
33
    /**
34
     * @var AuthorizationCheckerInterface
35
     */
36
    private $authorizationChecker;
37
38
    public function __construct(
39
        ManagerInterface $manager,
40
        TokenStorageInterface $tokenStorage,
41
        AuthorizationCheckerInterface $authorizationChecker
42
    ) {
43
        $this->manager = $manager;
44
        $this->tokenStorage = $tokenStorage;
45
        $this->authorizationChecker = $authorizationChecker;
46
    }
47
48
    /**
49
     * {@inheritdoc}
50
     */
51
    public function addElement(Element $element): void
52
    {
53
        $this->manager->addElement($element);
54
    }
55
56
    /**
57
     * {@inheritdoc}
58
     */
59
    public function hasElement(string $id): bool
60
    {
61
        if (!$this->manager->hasElement($id)) {
62
            return false;
63
        }
64
65
        $element = $this->manager->getElement($id);
66
        if ($this->isAccessToElementRestricted($element)) {
67
            return false;
68
        }
69
70
        return true;
71
    }
72
73
    /**
74
     * @param string $id
75
     * @return Element
76
     * @throws RuntimeException
77
     * @throws AccessDeniedException
78
     */
79
    public function getElement(string $id): Element
80
    {
81
        $element = $this->manager->getElement($id);
82
        if (!$element) {
83
            throw new RuntimeException(
0 ignored issues
show
Bug introduced by
The type FSi\Bundle\AdminSecurity...\Admin\RuntimeException was not found. Did you mean RuntimeException? If so, make sure to prefix the type with \.
Loading history...
84
                sprintf('Element with id "%s" does not exist', $id)
85
            );
86
        }
87
88
        if ($this->isAccessToElementRestricted($element)) {
89
            throw new AccessDeniedException(sprintf(
90
                'Access denied to element "%s"',
91
                get_class($element)
92
            ));
93
        }
94
95
        return $element;
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101
    public function removeElement(string $id): void
102
    {
103
        $this->manager->removeElement($id);
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109
    public function getElements(): array
110
    {
111
        return array_filter((array) $this->manager->getElements(), function (Element $element) {
112
            return !$this->isAccessToElementRestricted($element);
113
        });
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119
    public function accept(Visitor $visitor): void
120
    {
121
        $visitor->visitManager($this);
122
    }
123
124
    /**
125
     * @param Element $element
126
     * @return boolean
127
     */
128
    private function isAccessToElementRestricted(Element $element): bool
129
    {
130
        if (!$this->tokenStorage->getToken()) {
131
            // The request is not behind a firewall, so all elements are restricted
132
            return true;
133
        }
134
135
        if ($this->isUserForcedToChangePassword()) {
136
            return true;
137
        }
138
139
        return  $element instanceof SecuredElementInterface
140
            && !$element->isAllowed($this->authorizationChecker)
141
        ;
142
    }
143
144
    /**
145
     * @return boolean
146
     */
147
    private function isUserForcedToChangePassword(): bool
148
    {
149
        if (!$this->authorizationChecker->isGranted('IS_AUTHENTICATED_FULLY')) {
150
            return false;
151
        }
152
153
        $user = $this->tokenStorage->getToken()->getUser();
154
        if (!($user instanceof EnforceablePasswordChangeInterface)) {
155
            return false;
156
        }
157
158
        return $user->isForcedToChangePassword();
159
    }
160
}
161