Completed
Push — master ( d67956...a656c9 )
by Piotr
11s
created

SecuredManager::hasElement()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
nc 3
nop 1
dl 0
loc 12
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\Security\User\EnforceablePasswordChangeInterface;
16
use RuntimeException;
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(
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