Symfony 2

The Security component


Marie Minasyan


20.06.2013

                  Introduction

The security component:

  • complete security system for an application
  • independent of Symfony2
  • facilities for authenticating using:

    • HTTP authentication
    • Login form
    • Certificate
    • Etc.

  • ways to authorize authenticated users based on their roles and ACLs

Security is a two-step process whose goal is to prevent a user from accessing a resource that he/she should not have access to.

  1. Authentication: identifies the user - firewalls
  2. Authorization: decides if he/she has permission to access a resource - access control
# app/config/security.yml
security:
firewalls:
secured_area:
pattern: ^/
anonymous: ~
http_basic:
realm: "Secured Demo Area"

access_control:
- { path: ^/admin, roles: ROLE_ADMIN }

providers:
in_memory:
memory:
users:
ryan: { password: ryanpass, roles: 'ROLE_USER' }
admin: { password: kitten, roles: 'ROLE_ADMIN' }

encoders:
Symfony\Component\Security\Core\User\User: plaintext


1. Anonymous access

../_images/security_anonymous_user_access.png


2. Anonymous - access denied

../_images/security_anonymous_user_denied_authorization.png


3. Authenticated - Access denied

../_images/security_ryan_no_role_admin_access.png


4. Authenticated - Access ok

../_images/security_admin_role_access.png


Roles

Each user is assigned a set of roles, each resource requires one or more roles. If the user has the required roles, access is granted. Otherwise access is denied.

Role names begin with 'ROLE_...'


Role inheritance:


# app/config/security.yml

security:
#...
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]


How it works?

Authenticating is done by the firewall

=> configured using a map of all secured areas of the application

  => the map contains a request matcher and a collection of listeners


The request matcher gives the firewall the ability to find out if the current request points to a secured area. The listeners are then asked if the current request can be used to authenticate the user.

  1. Firewall => listener on the kernel.request
  2. Should any firewall be active for this URL?
  3. If yes, its listeners are notified
  4. Each listener may
    (a) authenticate a user,

    (b) throw an AuthenticationException ,
    (c) do nothing
  5. => Authorization


Access controls (1)

For each incoming request:

  1. Symfony2 checks each access control entry
  2. Stops as soon as finds one corresponding entry

Options:

  • path
  • ip or ips
  • host
  • methods
  • requires_channel

             Access controls (1)


# app/config/security.yml
security:
    #...
    access_control:
       - { path: ^/admin, roles: ROLE_USER, ip: 127.0.0.1 }
       - { path: ^/admin, roles: ROLE_USER_HOST, host: symfony.com }
       - { path: ^/admin, roles: ROLE_USER_METHOD, methods: [POST, PUT] }
       - { path: ^/admin, roles: ROLE_USER}
       - { path: ^/cart, roles: ROLE_USER, requires_channel: https}

/admin/users     127.0.0.1     example.com     GET => rule #1
/admin/user     127.0.0.1     symfony.com     GET => rule #1
/admin/user     168.0.0.1     symfony.com     GET => rule #2
/admin/user     168.0.0.1     symfony.com     POST => rule #2
/admin/user     168.0.0.1     example.com     POST => rule #3
/admin/user     168.0.0.1     example.com     GET => rule #4


Access controls (2)

Controllers:

/**
* @Secure(roles="ROLE_ADMIN")
**/

public function helloAction($name) { }
public function helloAction($name)
{
if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException();
}
}
{% if is_granted('ROLE_ADMIN') %}
<a href="...">Edit</a>
{% endif %}



User providers (1)

  1. The user submits a set of credentials.
  2. The authentication system matches those credentials against some pool of users.

Users can come from anywhere - a configuration file, a database table, a web service, or anything else.

              User providers (2)

Most common : from DB using Doctrine 2

# app/config/security.yml
security
providers:
main:
entity: { class: Acme\UserBundle\Entity\User, property: username }

// src/Acme/UserBundle/Entity/User.php
namespace Acme\UserBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
/** * @ORM\Entity */
class User implements UserInterface
{
/** * @ORM\Column(type="string", length=255) */
protected $username;
}

                User providers (3)

Example  with a web service:

# app/config/security.yml
security
providers:
users:
id: user.provider

<service id="user.provider" class="Acme\UserBundle\Entity\Manager\UserManager">
<argument type="service" id="service_container"/>
</service>


  1. UserManager implements UserProviderInterface - retrieves and refreshes users
  2. Token extends AbstractToken - represents the user authentication data present in the request
  3. Listener extends  AbstractAuthenticationListener - fielding requests to the firewall and calling the authentication provider
  4. AuthenticationProvider implements  AuthenticationProviderInterface - verifies theToken
  5. Factory extends SecurityFactoryInterface - ties them all together
  6. Factory definition in main Bundle


Multiple providers

Create a new provider (declared first) that chains several providers together :


# 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 }


Multiple providers and firewalls



# app/config/security.yml

security:
firewalls:
secured_area:
# ...
provider: user_db
http_basic:
realm: "Secured Demo Area"
provider: in_memory
form_login: ~



Encoding users passwords


# app/config/security.yml

security:
# ...
encoders:
Symfony\Component\Security\Core\User\User:
algorithm: sha1
iterations: 1
encode_as_base64: false
Acme\UserBundle\Entity\User: sha512


$factory = $this->get('security.encoder_factory');
$user = new Acme\UserBundle\Entity\User();

$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword('ryanpass', $user->getSalt());
$user->setPassword($password);



Conclusion


Security = Authentication + 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.

Once a user is authenticated, the authorization layer can determine whether or not the user should have access to a specific resource.


Questions?


Symfony 2 - Security

By Marie Minasyan

Symfony 2 - Security

  • 3,622