10000 feature #19473 [Security] Expose the required roles in AccessDeniedEx… · symfony/symfony@90f7ff5 · GitHub
[go: up one dir, main page]

Skip to content

Commit 90f7ff5

Browse files
committed
feature #19473 [Security] Expose the required roles in AccessDeniedException (Nicofuma)
This PR was merged into the 3.2-dev branch. Discussion ---------- [Security] Expose the required roles in AccessDeniedException | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | License | MIT Nowadays it is more and more common to protect some sensitive actions and part of a website using 2FA or some re-authentication mechanism (per example, on Github you have to enter your password again when you add an ssh key). But currently, in Symfony, it is really hard to implement without having to duplicate the logic, provide an explicit list of URLs to protect or hack into the security component. A good way to achieve that would be to add a special role (like IS_AUTHENTICATED_FULLY) and use it in the access map. But it requires us to be able to have a custom logic in an ExceptionListener depending on the roles behind an AccessDeniedException. With this patch we could write an ExceptionListener of this kind (a similar logic could also be used in an AccessDeniedHandler): ```php public function onKernelException(GetResponseForExceptionEvent $event) { $exception = $event->getException(); do { if ($exception instanceof AccessDeniedException) { foreach ($exception->getAttributes() as $role) { if ($role === 'IS_AUTHENTICATED_2FA' && !$this->accessDecisionManager->decide($this->tokenStorage->getToken(), $role, $exception->getObject())) { // Start 2FA } } } } while (null !== $exception = $exception->getPrevious()); } ``` Replaces #18661 Commits ------- 6618c18 [Security] Expose the required roles in AccessDeniedException
2 parents 02f59fe + 6618c18 commit 90f7ff5

File tree

7 files changed

+56
-5
lines changed

7 files changed

+56
-5
lines changed

src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,11 @@ protected function isGranted($attributes, $object = null)
192192
protected function denyAccessUnlessGranted($attributes, $object = null, $message = 'Access Denied.')
193193
{
194194
if (!$this->isGranted($attributes, $object)) {
195-
throw $this->createAccessDeniedException($message);
195+
$exception = $this->createAccessDeniedException($message);
196+
$exception->setAttributes($attributes);
197+
$exception->setObject($object);
198+
199+
throw $exception;
196200
}
197201
}
198202

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"symfony/filesystem": "~2.8|~3.0",
3030
"symfony/finder": "~2.8|~3.0",
3131
"symfony/routing": "~3.0",
32-
"symfony/security-core": "~2.8|~3.0",
32+
"symfony/security-core": "~3.2",
3333
"symfony/security-csrf": "~2.8|~3.0",
3434
"symfony/stopwatch": "~2.8|~3.0",
3535
"symfony/templating": "~2.8|~3.0",

src/Symfony/Component/Security/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
3.2.0
5+
-----
6+
7+
* added `attributes` and `object` with getters/setters to `Symfony\Component\Security\Core\Exception\AccessDeniedException`
8+
49
3.0.0
510
-----
611

src/Symfony/Component/Security/Core/Exception/AccessDeniedException.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,43 @@
1818
*/
1919
class AccessDeniedException extends \RuntimeException
2020
{
21+
private $attributes = array();
22+
private $object;
23+
2124
public function __construct($message = 'Access Denied.', \Exception $previous = null)
2225
{
2326
parent::__construct($message, 403, $previous);
2427
}
28+
29+
/**
30+
* @return array
31+
*/
32+
public function getAttributes()
33+
{
34+
return $this->attributes;
35+
}
36+
37+
/**
38+
* @param array|string $attributes
39+
*/
40+
public function setAttributes($attributes)
41+
{
42+
$this->attributes = (array) $attributes;
43+
}
44+
45+
/**
46+
* @return mixed
47+
*/
48+
public function getObject()
49+
{
50+
return $this->object;
51+
}
52+
53+
/**
54+
* @param mixed $object
55+
*/
56+
public function setObject($object)
57+
{
58+
$this->object = $object;
59+
}
2560
}

src/Symfony/Component/Security/Http/Firewall/AccessListener.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,11 @@ public function handle(GetResponseEvent $event)
6767
}
6868

6969
if (!$this->accessDecisionManager->decide($token, $attributes, $request)) {
70-
throw new AccessDeniedException();
70+
$exception = new AccessDeniedException();
71+
$exception->setAttributes($attributes);
72+
$exception->setObject($request);
73+
74+
throw $exception;
7175
}
7276
}
7377
}

src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,10 @@ private function attemptSwitchUser(Request $request)
122122
}
123123

124124
if (false === $this->accessDecisionManager->decide($token, array($this->role))) {
125-
throw new AccessDeniedException();
125+
$exception = new AccessDeniedException();
126+
$exception->setAttributes($this->role);
127+
128+
throw $exception;
126129
}
127130

128131
$username = $request->get($this->usernameParameter);

src/Symfony/Component/Security/Http/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
],
1818
"require": {
1919
"php": ">=5.5.9",
20-
"symfony/security-core": "~2.8|~3.0",
20+
"symfony/security-core": "~3.2",
2121
"symfony/event-dispatcher": "~2.8|~3.0",
2222
"symfony/http-foundation": "~2.8|~3.0",
2323
"symfony/http-kernel": "~2.8|~3.0",

0 commit comments

Comments
 (0)
0