`
method:
.. configuration-block::
.. code-block:: html+jinja
Username: {{ app.user.username }}
.. code-block:: html+php
Username: getUser()->getUsername() ?>
Using multiple User Providers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Each authentication mechanism (e.g. HTTP Authentication, form login, etc)
uses exactly one user provider, and will use the first declared user provider
by default. But what if you want to specify a few users via configuration
and the rest of your users in the database? This is possible by creating
a new provider that chains the two together:
.. configuration-block::
.. code-block:: yaml
# app/config/security.yml
security:
providers:
chain_provider:
chain:
providers: [in_memory, user_db]
in_memory:
memory:
users:
foo: { password: test }
user_db:
entity: { class: Acme\UserBundle\Entity\User, property: username }
.. code-block:: xml
in_memory
user_db
.. code-block:: php
// app/config/security.php
$container->loadFromExtension('security', array(
'providers' => array(
'chain_provider' => array(
'chain' => array(
'providers' => array('in_memory', 'user_db'),
),
),
'in_memory' => array(
'memory' => array(
'users' => array(
'foo' => array('password' => 'test'),
),
),
),
'user_db' => array(
'entity' => array(
'class' => 'Acme\UserBundle\Entity\User',
'property' => 'username',
),
),
),
));
Now, all authentication mechanisms will use the ``chain_provider``, since
it's the first specified. The ``chain_provider`` will, in turn, try to load
the user from both the ``in_memory`` and ``user_db`` providers.
You can also configure the firewall or individual authentication mechanisms
to use a specific provider. Again, unless a provider is specified explicitly,
the first provider is always used:
.. configuration-block::
.. code-block:: yaml
# app/config/security.yml
security:
firewalls:
secured_area:
# ...
provider: user_db
http_basic:
realm: "Secured Demo Area"
provider: in_memory
form_login: ~
.. code-block:: xml
.. code-block:: php
// app/config/security.php
$container->loadFromExtension('security', array(
'firewalls' => array(
'secured_area' => array(
// ...
'provider' => 'user_db',
'http_basic' => array(
// ...
'provider' => 'in_memory',
),
'form_login' => array(),
),
),
));
In this example, if a user tries to log in via HTTP authentication, the authentication
system will use the ``in_memory`` user provider. But if the user tries to
log in via the form login, the ``user_db`` provider will be used (since it's
the default for the firewall as a whole).
For more information about user provider and firewall configuration, see
the :doc:`/reference/configuration/security`.
Roles
-----
The idea of a "role" is key to the authorization process. Each user is assigned
a set of roles and then each resource requires one or more roles. If the user
has any one of the required roles, access is granted. Otherwise access is denied.
Roles are pretty simple, and are basically strings that you can invent and
use as needed (though roles are objects internally). For example, if you
need to start limiting access to the blog admin section of your website,
you could protect that section using a ``ROLE_BLOG_ADMIN`` role. This role
doesn't need to be defined anywhere - you can just start using it.
.. note::
All roles **must** begin with the ``ROLE_`` prefix to be managed by
Symfony2. If you define your own roles with a dedicated ``Role`` class
(more advanced), don't use the ``ROLE_`` prefix.
.. _book-security-role-hierarchy:
Hierarchical Roles
~~~~~~~~~~~~~~~~~~
Instead of associating many roles to users, you can define role inheritance
rules by creating a role hierarchy:
.. configuration-block::
.. code-block:: yaml
# app/config/security.yml
security:
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
.. code-block:: xml
ROLE_USER
ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH
.. code-block:: php
// app/config/security.php
$container->loadFromExtension('security', array(
'role_hierarchy' => array(
'ROLE_ADMIN' => 'ROLE_USER',
'ROLE_SUPER_ADMIN' => array(
'ROLE_ADMIN',
'ROLE_ALLOWED_TO_SWITCH',
),
),
));
In the above configuration, users with ``ROLE_ADMIN`` role will also have the
``ROLE_USER`` role. The ``ROLE_SUPER_ADMIN`` role has ``ROLE_ADMIN``, ``ROLE_ALLOWED_TO_SWITCH``
and ``ROLE_USER`` (inherited from ``ROLE_ADMIN``).
Access Control
--------------
Now that you have a User and Roles, you can go further than URL-pattern based
authorization.
Access Control in Controllers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Protecting your application based on URL patterns is easy, but may not be
fine-grained enough in certain cases. When necessary, you can easily force
authorization from inside a controller::
// ...
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
public function helloAction($name)
{
if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException();
}
// ...
}
.. caution::
A firewall must be active or an exception will be thrown when the ``isGranted()``
method is called. It's almost always a good idea to have a main firewall that
covers all URLs (as is shown in this chapter).
.. _book-security-expressions:
Complex Access Controls with Expressions
----------------------------------------
.. versionadded:: 2.4
The expression functionality was introduced in Symfony 2.4.
In addition to a role like ``ROLE_ADMIN``, the ``isGranted`` method also
accepts an :class:`Symfony\\Component\\ExpressionLanguage\\Expression` object::
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\ExpressionLanguage\Expression;
// ...
public function indexAction()
{
if (!$this->get('security.context')->isGranted(new Expression(
'"ROLE_ADMIN" in roles or (user and user.isSuperAdmin())'
))) {
throw new AccessDeniedException();
}
// ...
}
In this example, if the current user has ``ROLE_ADMIN`` or if the current
user object's ``isSuperAdmin()`` method returns ``true``, then access will
be granted (note: your User object may not have an ``isSuperAdmin`` method,
that method is invented for this example).
This uses an expression and you can learn more about the expression language
syntax, see :doc:`/components/expression_language/syntax`.
.. _book-security-expression-variables:
Inside the expression, you have access to a number of variables:
* ``user`` The user object (or the string ``anon`` if you're not authenticated);
* ``roles`` The array of roles the user has, including from the
:ref:`role hierarchy ` but not including
the ``IS_AUTHENTICATED_*`` attributes (see the functions below);
* ``object``: The object (if any) that's passed as the second argument to
``isGranted`` ;
* ``token`` The token object;
* ``trust_resolver``: The :class:`Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationTrustResolverInterface`,
object: you'll probably use the ``is_*`` functions below instead.
Additionally, you have access to a number of functions inside the expression:
* ``is_authenticated``: Returns ``true`` if the user is authenticated via "remember-me"
or authenticated "fully" - i.e. returns true if the user is "logged in";
* ``is_anonymous``: Equal to using ``IS_AUTHENTICATED_ANONYMOUSLY`` with
the ``isGranted`` function;
* ``is_remember_me``: Similar, but not equal to ``IS_AUTHENTICATED_REMEMBERED``,
see below;
* ``is_fully_authenticated``: Similar, but not equal to ``IS_AUTHENTICATED_FULLY``,
see below;
* ``has_role``: Checks to see if the user has the given role - equivalent
to an expression like ``'ROLE_ADMIN' in roles``.
.. sidebar:: ``is_remember_me`` is different than checking ``IS_AUTHENTICATED_REMEMBERED``
The ``is_remember_me`` and ``is_authenticated_fully`` functions are *similar*
to using ``IS_AUTHENTICATED_REMEMBERED`` and ``IS_AUTHENTICATED_FULLY``
with the ``isGranted`` function - but they are **not** the same. The
following shows the difference::
use Symfony\Component\ExpressionLanguage\Expression;
// ...
$sc = $this->get('security.context');
$access1 = $sc->isGranted('IS_AUTHENTICATED_REMEMBERED');
$access2 = $sc->isGranted(new Expression(
'is_remember_me() or is_fully_authenticated()'
));
Here, ``$access1`` and ``$access2`` will be the same value. Unlike the
behavior of ``IS_AUTHENTICATED_REMEMBERED`` and ``IS_AUTHENTICATED_FULLY``,
the ``is_remember_me`` function *only* returns true if the user is authenticated
via a remember-me cookie and ``is_fully_authenticated`` *only* returns
true if the user has actually logged in during this session (i.e. is
full-fledged).
Access Control in Other Services
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In fact, anything in Symfony can be protected using a strategy similar to
the one seen in the previous section. For example, suppose you have a service
(i.e. a PHP class) whose job is to send emails from one user to another.
You can restrict use of this class - no matter where it's being used from -
to users that have a specific role.
For more information on how you can use the Security component to secure
different services and methods in your application, see :doc:`/cookbook/security/securing_services`.
.. _book-security-template:
Access Control in Templates
~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you want to check if the current user has a role inside a template, use
the built-in helper function:
.. configuration-block::
.. code-block:: html+jinja
{% if is_granted('ROLE_ADMIN') %}
Delete
{% endif %}
.. code-block:: html+php
isGranted('ROLE_ADMIN')): ?>
Delete
.. note::
If you use this function and are *not* at a URL behind a firewall
active, an exception will be thrown. Again, it's almost always a good
idea to have a main firewall that covers all URLs (as has been shown
in this chapter).
.. _book-security-template-expression:
.. versionadded:: 2.4
The ``expression`` functionality was introduced in Symfony 2.4.
You can also use expressions inside your templates:
.. configuration-block::
.. code-block:: html+jinja
{% if is_granted(expression(
'"ROLE_ADMIN" in roles or (user and user.isSuperAdmin())'
)) %}
Delete
{% endif %}
.. code-block:: html+php
isGranted(new Expression(
'"ROLE_ADMIN" in roles or (user and user.isSuperAdmin())'
))): ?>
Delete
For more details on expressions and security, see :ref:`book-security-expressions`.
Access Control Lists (ACLs): Securing individual Database Objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Imagine you are designing a blog system where your users can comment on your
posts. Now, you want a user to be able to edit their own comments, but not
those of other users. Also, as the admin user, you yourself want to be able
to edit *all* comments.
The Security component comes with an optional access control list (ACL) system
that you can use when you need to control access to individual instances
of an object in your system. *Without* ACL, you can secure your system so that
only certain users can edit blog comments in general. But *with* ACL, you
can restrict or allow access on a comment-by-comment basis.
For more information, see the cookbook article: :doc:`/cookbook/security/acl`.
.. _book-security-logging-out:
Logging Out
-----------
Usually, you'll also want your users to be able to log out. Fortunately,
the firewall can handle this automatically for you when you activate the
``logout`` config parameter:
.. configuration-block::
.. code-block:: yaml
# app/config/security.yml
security:
firewalls:
secured_area:
# ...
logout:
path: /logout
target: /
# ...
.. code-block:: xml
.. code-block:: php
// app/config/security.php
$container->loadFromExtension('security', array(
'firewalls' => array(
'secured_area' => array(
// ...
'logout' => array('path' => 'logout', 'target' => '/'),
),
),
// ...
));
Once this is configured under your firewall, sending a user to ``/logout``
(or whatever you configure the ``path`` to be), will un-authenticate the
current user. The user will then be sent to the homepage (the value defined
by the ``target`` parameter). Both the ``path`` and ``target`` config parameters
default to what's specified here. In other words, unless you need to customize
them, you can omit them entirely and shorten your configuration:
.. configuration-block::
.. code-block:: yaml
logout: ~
.. code-block:: xml
.. code-block:: php
'logout' => array(),
Note that you will *not* need to implement a controller for the ``/logout``
URL as the firewall takes care of everything. You *do*, however, need to create
a route so that you can use it to generate the URL:
.. configuration-block::
.. code-block:: yaml
# app/config/routing.yml
logout:
path: /logout
.. code-block:: xml
.. code-block:: php
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
$collection = new RouteCollection();
$collection->add('logout', new Route('/logout', array()));
return $collection;
Once the user has been logged out, they will be redirected to whatever path
is defined by the ``target`` parameter above (e.g. the ``homepage``). For
more information on configuring the logout, see the
:doc:`Security Configuration Reference `.
Stateless Authentication
------------------------
By default, Symfony2 relies on a cookie (the Session) to persist the security
context of the user. But if you use certificates or HTTP authentication for
instance, persistence is not needed as credentials are available for each
request. In that case, and if you don't need to store anything else between
requests, you can activate the stateless authentication (which means that no
cookie will be ever created by Symfony2):
.. configuration-block::
.. code-block:: yaml
# app/config/security.yml
security:
firewalls:
main:
http_basic: ~
stateless: true
.. code-block:: xml
.. code-block:: php
// app/config/security.php
$container->loadFromExtension('security', array(
'firewalls' => array(
'main' => array('http_basic' => array(), 'stateless' => true),
),
));
.. note::
If you use a form login, Symfony2 will create a cookie even if you set
``stateless`` to ``true``.
Utilities
---------
The Symfony Security component comes with a collection of nice utilities related
to security. These utilities are used by Symfony, but you should also use
them if you want to solve the problem they address.
Comparing Strings
~~~~~~~~~~~~~~~~~
The time it takes to compare two strings depends on their differences. This
can be used by an attacker when the two strings represent a password for
instance; it is known as a `Timing attack`_.
Internally, when comparing two passwords, Symfony uses a constant-time
algorithm; you can use the same strategy in your own code thanks to the
:class:`Symfony\\Component\\Security\\Core\\Util\\StringUtils` class::
use Symfony\Component\Security\Core\Util\StringUtils;
// is password1 equals to password2?
$bool = StringUtils::equals($password1, $password2);
Generating a secure random Number
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Whenever you need to generate a secure random number, you are highly
encouraged to use the Symfony
:class:`Symfony\\Component\\Security\\Core\\Util\\SecureRandom` class::
use Symfony\Component\Security\Core\Util\SecureRandom;
$generator = new SecureRandom();
$random = $generator->nextBytes(10);
The
:method:`Symfony\\Component\\Security\\Core\\Util\\SecureRandom::nextBytes`
methods returns a random string composed of the number of characters passed as
an argument (10 in the above example).
The SecureRandom class works better when OpenSSL is installed but when it's
not available, it falls back to an internal algorithm, which needs a seed file
to work correctly. Just pass a file name to enable it::
$generator = new SecureRandom('/some/path/to/store/the/seed.txt');
$random = $generator->nextBytes(10);
.. note::
You can also access a secure random instance directly from the Symfony
dependency injection container; its name is ``security.secure_random``.
Final Words
-----------
Security can be a deep and complex issue to solve correctly in your application.
Fortunately, Symfony's Security component follows a well-proven security
model based around *authentication* and *authorization*. Authentication,
which always happens first, is handled by a firewall whose job is to determine
the identity of the user through several different methods (e.g. HTTP authentication,
login form, etc). In the cookbook, you'll find examples of other methods
for handling authentication, including how to implement a "remember me" cookie
functionality.
Once a user is authenticated, the authorization layer can determine whether
or not the user should have access to a specific resource. Most commonly,
*roles* are applied to URLs, classes or methods and if the current user
doesn't have that role, access is denied. The authorization layer, however,
is much deeper, and follows a system of "voting" so that multiple parties
can determine if the current user should have access to a given resource.
Find out more about this and other topics in the cookbook.
Learn more from the Cookbook
----------------------------
* :doc:`Forcing HTTP/HTTPS `
* :doc:`Impersonating a User `
* :doc:`Blacklist users by IP address with a custom voter `
* :doc:`Access Control Lists (ACLs) `
* :doc:`/cookbook/security/remember_me`
* :doc:`How to Restrict Firewalls to a Specific Request `
.. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle
.. _`implement the \Serializable interface`: http://php.net/manual/en/class.serializable.php
.. _`Timing attack`: http://en.wikipedia.org/wiki/Timing_attack