Skip to content

Commit d11c0d0

Browse files
Added third chapter, on authorization
1 parent 0e3560a commit d11c0d0

File tree

4 files changed

+272
-26
lines changed

4 files changed

+272
-26
lines changed

components/security/authentication.rst

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ do is ask the authentication manager to validate the given token, and return
1212
an authenticated token when the supplied credentials were found to be valid.
1313
The listener should then store the authenticated token in the security context:
1414

15-
.. code-block:: php
15+
::
1616

1717
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
1818
use Symfony\Component\Security\Core\SecurityContextInterface;
@@ -22,10 +22,14 @@ The listener should then store the authenticated token in the security context:
2222

2323
class SomeAuthenticationListener implements ListenerInterface
2424
{
25-
/* @var SecurityContextInterface */
25+
/**
26+
* @var SecurityContextInterface
27+
*/
2628
private $securityContext;
2729

28-
/* @var AuthenticationManagerInterface */
30+
/**
31+
* @var AuthenticationManagerInterface
32+
*/
2933
private $authenticationManager;
3034

3135
// string Uniquely identifies the secured area
@@ -37,6 +41,9 @@ The listener should then store the authenticated token in the security context:
3741
{
3842
$request = $event->getRequest();
3943

44+
$username = ...;
45+
$password = ...;
46+
4047
$unauthenticatedToken = new UsernamePasswordToken(
4148
$username,
4249
$password,
@@ -60,13 +67,12 @@ The authentication manager
6067

6168
The default authentication manager is an instance of :class:`Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationProviderManager`:
6269

63-
.. code-block:: php
70+
::
6471

6572
use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager;
6673

67-
$providers = array(
68-
// instances of Symfony\Component\Security\Core\Authentication\AuthenticationProviderInterface
69-
);
74+
// instances of Symfony\Component\Security\Core\Authentication\AuthenticationProviderInterface
75+
$providers = array(...);
7076

7177
$authenticationManager = new AuthenticationProviderManager($providers);
7278

@@ -85,6 +91,8 @@ authentication providers, each supporting a different type of token.
8591
You may of course write your own authentication manager, it only has
8692
to implement :class:`Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationManagerInterface`.
8793

94+
.. _authentication_providers:
95+
8896
Authentication providers
8997
------------------------
9098

@@ -113,7 +121,7 @@ It fetches the user's data from a ``UserProvider``, uses a ``PasswordEncoder``
113121
to create a hash of the password and returns an authenticated token if the
114122
password was valid.
115123

116-
.. code-block:: php
124+
::
117125

118126
use Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider;
119127
use Symfony\Component\Security\Core\User\UserChecker;
@@ -131,7 +139,8 @@ password was valid.
131139
// for some extra checks: is account enabled, locked, expired, etc.?
132140
$userChecker = new UserChecker();
133141

134-
$encoderFactory = new EncoderFactory(/* ... encoders */);
142+
// an array of password encoders (see below)
143+
$encoderFactory = new EncoderFactory(...);
135144

136145
$provider = new DaoAuthenticationProvider(
137146
$userProvider,
@@ -159,18 +168,25 @@ strategies for different types of users.
159168
The default :class:`Symfony\\Component\\Security\\Core\\Encoder\\EncoderFactory`
160169
receives an array of encoders:
161170

162-
.. code-block:: php
171+
::
163172

164173
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
165174
use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder;
166175

167176
$defaultEncoder = new MessageDigestPasswordEncoder('sha512', true, 5000);
168177
$weakEncoder = new MessageDigestPasswordEncoder('md5', true, 1);
169178

170-
$encoderFactory = new EncoderFactory(array(
179+
$encoders = array(
171180
'Symfony\\Component\\Security\\Core\\User\\User' => $defaultEncoder,
172181
'Acme\\Entity\\LegacyUser' => $weakEncoder,
173-
));
182+
...
183+
);
184+
185+
$encoderFactory = new EncoderFactory($encoders);
186+
187+
Each encoder should implement :class:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface`
188+
or be an array with a ``class`` and an ``arguments`` key, which allows the
189+
encoder factory to construct the encoder only when it is needed.
174190

175191
Password encoders
176192
~~~~~~~~~~~~~~~~~
@@ -180,17 +196,18 @@ with the user object as its first argument, it will return an encoder of
180196
type :class:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface`
181197
which should be used to encode this user's password:
182198

183-
.. code-block:: php
199+
::
184200

185-
$user = // ... fetch a user of type Acme\Entity\LegacyUser
201+
// fetch a user of type Acme\Entity\LegacyUser
202+
$user = ...
186203

187204
$encoder = $encoderFactory->getEncoder($user);
188205

189206
// will return $weakEncoder (see above)
190207

191208
$encodedPassword = $encoder->encodePassword($password, $user->getSalt());
192209

193-
// or check if the password is valid:
210+
// check if the password is valid:
194211

195212
$validPassword = $encoder->isPasswordValid(
196213
$user->getPassword(),

components/security/authorization.rst

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
.. index::
2+
single: Security, Authorization
3+
4+
Authorization
5+
=============
6+
7+
When any of the authentication providers (see :ref:`authentication_providers`)
8+
has verified the still unauthenticated token, an authenticated token will
9+
be returned. The authentication listener should set this token directly
10+
in the :class:`Symfony\\Component\\Security\\Core\\SecurityContext` using its
11+
``setToken()`` method.
12+
13+
From then on, the user is authenticated, i.e. means identified.
14+
Now, other parts of the application can use the token to decide whether
15+
or not the user may request a certain URI, or modify a certain object.
16+
This decision will be made by an instance of :class:`Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManagerInterface`.
17+
18+
An authorization decision will always be based on a few things:
19+
20+
The current token
21+
The token`s ``getRoles()`` method will be used to retrieve the roles
22+
of the current user (e.g. "ROLE_SUPER_ADMIN")
23+
A set of attributes
24+
Each attribute stands for a certain right the user should have, e.g.
25+
"ROLE_ADMIN" to make sure the user is an administrator.
26+
An object (optional)
27+
Any object on which to decide, e.g. the current :class:`Symfony\\Component\\HttpFoundation\\Request`
28+
object.
29+
30+
Access decision manager
31+
-----------------------
32+
33+
Since choosing whether or not a user is authorized to perform a certain
34+
action can be a complicated process, the standard :class:`Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionManager`
35+
itself depends on multiple voters, and makes a final verdict based on all
36+
the votes (either positive, negative or neutral) it has received. It
37+
recognizes several strategies:
38+
39+
``affirmative`` (default)
40+
Grant access as soon as any voter returns an affirmative response
41+
42+
``consensus``
43+
Grant access if there are more voters granting access then there are denying
44+
45+
``unanimous``
46+
Only grant access if none of the voters has denied access
47+
48+
::
49+
50+
use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
51+
52+
// instances of Symfony\Component\Security\Core\Authorization\Voter\VoterInterface
53+
$voters = array(...);
54+
55+
// one of "affirmative", "consensus", "unanimous"
56+
$strategy = ...;
57+
58+
// whether or not to grant access when all voters abstain
59+
$allowIfAllAbstainDecisions = ...;
60+
61+
// whether or not to grant access when there is no majority (applies only to the "consensus" strategy)
62+
$allowIfEqualGrantedDeniedDecisions = ...;
63+
64+
$accessDecisionManager = new AccessDecisionManager($voters, $strategy,
65+
$allowIfAllAbstainDecisions, $allowIfEqualGrantedDeniedDecisions);
66+
67+
Voters
68+
------
69+
70+
Voters are instances
71+
of :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`,
72+
which means they have to implement a few methods which allows the decision
73+
manager to use them:
74+
75+
``supportsAttribute($attribute)``
76+
Will be used to check if the voter knows how to handle the given attribute.
77+
``supportsClass($class)``
78+
Will be used to check if the voter is able to grant or deny access for
79+
an object of the given class.
80+
``vote(TokenInterface $token, $object, array $attributes)``
81+
This method will do the actual voting and return a value equal to one
82+
of the class constants of :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`,
83+
i.e. ``VoterInterface::ACCESS_GRANTED``, ``VoterInterface::ACCESS_DENIED``
84+
or ``VoterInterface::ACCESS_ABSTAIN``.
85+
86+
The security component contains some standard voters which cover many use
87+
cases:
88+
89+
The :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AuthenticatedVoter`
90+
voter supports the attributes "IS_AUTHENTICATED_FULLY", "IS_AUTHENTICATED_REMEMBERED",
91+
and "IS_AUTHENTICATED_ANONYMOUSLY" and grants access based on the current
92+
level of authentication, i.e. is the user fully authenticated, or only based
93+
on a "remember-me" cookie, or even authenticated anonymously?
94+
95+
::
96+
97+
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver;
98+
99+
$anonymousClass = 'Symfony\Component\Security\Core\Authentication\Token\AnonymousToken';
100+
$rememberMeClass = 'Symfony\Component\Security\Core\Authentication\Token\RememberMeToken';
101+
102+
$trustResolver = new AuthenticationTrustResolver($anonymousClass, $rememberMeClass);
103+
104+
$authenticatedVoter = new AuthenticatedVoter($trustResolver);
105+
106+
// instance of Symfony\Component\Security\Core\Authentication\Token\TokenInterface
107+
$token = ...
108+
109+
// any object
110+
$object = ...
111+
112+
$vote = $authenticatedVoter->vote($token, $object, array('IS_AUTHENTICATED_FULLY');
113+
114+
The :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\RoleVoter`
115+
supports attributes starting with "ROLE_" and grants access to the user
116+
when the required "ROLE_*" attributes can all be found in the array of
117+
roles returned by the token's ``getRoles()`` method.
118+
119+
::
120+
121+
use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter;
122+
123+
$roleVoter = new RoleVoter('ROLE_');
124+
125+
$roleVoter->vote($token, $object, 'ROLE_ADMIN');
126+
127+
The :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\RoleHierarchyVoter`
128+
extends :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\RoleVoter`
129+
and provides some additional functionality: it knows how to handle a
130+
hierarchy of roles. For instance, a "ROLE_SUPER_ADMIN" role may have subroles
131+
"ROLE_ADMIN" and "ROLE_USER", so that when a certain object requires the
132+
user to have the "ROLE_ADMIN" role, it grants access to users who in fact
133+
have the "ROLE_ADMIN" role, but also to users having the "ROLE_SUPER_ADMIN"
134+
role.
135+
136+
::
137+
138+
use Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter;
139+
use Symfony\Component\Security\Core\Role\RoleHierarchy;
140+
141+
$hierarchy = array(
142+
'ROLE_SUPER_ADMIN' => array('ROLE_ADMIN', 'ROLE_USER'),
143+
);
144+
145+
$roleHierarchy = new RoleHierarchy($hierarchy);
146+
147+
$roleHierarchyVoter = new RoleHierarchyVoter($roleHierarchy);
148+
149+
.. note::
150+
151+
When you make your own voter, you may of course use its constructor
152+
to inject any dependencies it needs to come to a decision.
153+
154+
Roles
155+
-----
156+
157+
Roles are objects that give expression to a certain right the user has.
158+
The only requirement is that they implement :class:`Symfony\\Component\\Security\\Core\\Role\\RoleInterface`,
159+
which means they should also have a ``getRole()`` method that returns a
160+
string representation of the role itself. The default :class:`Symfony\\Component\\Security\\Core\\Role\\Role`
161+
simply returns its first constructor argument:
162+
163+
::
164+
165+
use Symfony\Component\Security\Core\Role\Role;
166+
167+
$role = new Role('ROLE_ADMIN');
168+
169+
// will echo 'ROLE_ADMIN'
170+
echo $role->getRole();
171+
172+
.. note::
173+
174+
Most authentication tokens extend from :class:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\AbstractToken`,
175+
which means that the roles given to its constructor, will be
176+
automatically converted from strings to these simple ``Role`` objects.
177+
178+
Using the decision manager
179+
--------------------------
180+
181+
The access listener
182+
~~~~~~~~~~~~~~~~~~~
183+
184+
Normally, the access decision manager will already be asked to decide whether
185+
or not the current user is entitled to make the current request. This is done
186+
by the :class:`Symfony\\Component\\Security\\Http\\Firewall\\AccessListener`,
187+
which is one of the firewall listeners (see :ref:`firewall_listeners`) that
188+
will be triggered for each request matching the firewall map (see :ref:`firewall`).
189+
190+
It uses an access map (which should be an instance of :class:`Symfony\\Component\\Security\\Http\\AccessMapInterface`)
191+
which contains request matchers and a corresponding set of attributes that
192+
are required for the current user to get access to the application.
193+
194+
::
195+
196+
use Symfony\Component\Security\Http\AccessMap;
197+
use Symfony\Component\HttpFoundation\RequestMatcher;
198+
use Symfony\Component\Security\Http\Firewall\AccessListener;
199+
200+
$accessMap = new AccessMap();
201+
$requestMatcher = new RequestMatcher('^/admin');
202+
$accessMap->add($requestMatcher, array('ROLE_ADMIN'));
203+
204+
$accessListener = new AccessListener($securityContext, $accessDecisionManager,
205+
$accessMap, $authenticationManager);
206+
207+
Security context
208+
~~~~~~~~~~~~~~~~
209+
210+
The access decision manager is also available to other parts of the application
211+
by means of the ``isGranted($attribute)`` method of the :class:`Symfony\\Component\\Security\\Core\\SecurityContext`.
212+
A call to this method will directly delegate the question to the access
213+
decision manager.
214+
215+
::
216+
217+
use Symfony\Component\Security\SecurityContext;
218+
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
219+
220+
$securityContext = new SecurityContext();
221+
222+
if (!$securityContext->isGranted('ROLE_ADMIN')) {
223+
throw new AccessDeniedException();
224+
}

0 commit comments

Comments
 (0)