The ability to execute an action can be restricted to users with certain privileges. The tools provided by symfony for this purpose allow the creation of secure applications, where users need to be authenticated before accessing some features or parts of the application. Securing an application requires two steps: declaring the security requirements for each action and logging in users with privileges so that they can access these secure actions.

6.5.1. Access Restriction

Before being executed, every action passes by a special filter that checks if the current user has the privileges to access the requested action. In symfony, privileges are composed of two parts:

  • Secure actions require users to be authenticated.
  • Credentials are named security privileges that allow organizing security by group.

Restricting access to an action is simply made by creating and editing a YAML configuration file called security.yml in the module config/ directory. In this file, you can specify the security requirements that users must fulfill for each action or for all actions. Listing 6-21 shows a sample security.yml.

Listing 6-21 - Setting Access Restrictions, in apps/frontend/modules/mymodule/config/security.yml

read:
  is_secure:   off       # All users can request the read action

update:
  is_secure:   on        # The update action is only for authenticated users

delete:
  is_secure:   on        # Only for authenticated users
  credentials: admin     # With the admin credential

all:
  is_secure:  off        # off is the default value anyway

Actions are not secure by default, so when there is no security.yml or no mention of an action in it, actions are accessible by everyone. If there is a security.yml, symfony looks for the name of the requested action and, if it exists, checks the fulfillment of the security requirements. What happens when a user tries to access a restricted action depends on his credentials:

  • If the user is authenticated and has the proper credentials, the action is executed.
  • If the user is not identified, he will be redirected to the default login action.
  • If the user is identified but doesn't have the proper credentials, he will be redirected to the default secure action, shown in Figure 6-1.

The default login and secure pages are pretty simple, and you will probably want to customize them. You can configure which actions are to be called in case of insufficient privileges in the application settings.yml by changing the value of the properties shown in Listing 6-22.

The default secure action page

Figure 6.1 The default secure action page

Listing 6-22 - Default Security Actions Are Defined in apps/frontend/config/settings.yml

all:
  .actions:
    login_module:  default
    login_action:  login

    secure_module: default
    secure_action: secure

6.5.2. Granting Access

To get access to restricted actions, users need to be authenticated and/or to have certain credentials. You can extend a user's privileges by calling methods of the sfUser object. The authenticated status of the user is set by the setAuthenticated() method and can be checked with isAuthenticated(). Listing 6-23 shows a simple example of user authentication.

Listing 6-23 - Setting the Authenticated Status of a User

class myAccountActions extends sfActions
{
  public function executeLogin($request)
  {
    if ($request->getParameter('login') == 'foobar')
    {
      $this->getUser()->setAuthenticated(true);
    }
  }

  public function executeLogout()
  {
    $this->getUser()->setAuthenticated(false);
  }
}

Credentials are a bit more complex to deal with, since you can check, add, remove, and clear credentials. Listing 6-24 describes the credential methods of the sfUser class.

Listing 6-24 - Dealing with User Credentials in an Action

class myAccountActions extends sfActions
{
  public function executeDoThingsWithCredentials()
  {
    $user = $this->getUser();

    // Add one or more credentials
    $user->addCredential('foo');
    $user->addCredentials('foo', 'bar');

    // Check if the user has a credential
    echo $user->hasCredential('foo');                      =>   true

    // Check if the user has both credentials
    echo $user->hasCredential(array('foo', 'bar'));        =>   true

    // Check if the user has one of the credentials
    echo $user->hasCredential(array('foo', 'bar'), false); =>   true

    // Remove a credential
    $user->removeCredential('foo');
    echo $user->hasCredential('foo');                      =>   false

    // Remove all credentials (useful in the logout process)
    $user->clearCredentials();
    echo $user->hasCredential('bar');                      =>   false
  }
}

If a user has the foo credential, that user will be able to access the actions for which the security.yml requires that credential. Credentials can also be used to display only authorized content in a template, as shown in Listing 6-25.

Listing 6-25 - Dealing with User Credentials in a Template

<ul>
  <li><?php echo link_to('section1', 'content/section1') ?></li>
  <li><?php echo link_to('section2', 'content/section2') ?></li>
  <?php if ($sf_user->hasCredential('section3')): ?>
  <li><?php echo link_to('section3', 'content/section3') ?></li>
  <?php endif; ?>
</ul>

As for the authenticated status, credentials are often given to users during the login process. This is why the sfUser object is often extended to add login and logout methods, in order to set the security status of users in a central place.

Tip Among the symfony plug-ins, the sfGuardPlugin (http://trac.symfony-project.org/wiki/sfGuardPlugin) extends the session class to make login and logout easy. Refer to Chapter 17 for more information.

6.5.3. Complex Credentials

The YAML syntax used in the security.yml file allows you to restrict access to users having a combination of credentials, using either AND-type or OR-type associations. With such a combination, you can build a complex workflow and user privilege management system--for instance, a content management system (CMS) back-office accessible only to users with the admin credential, where articles can be edited only by users with the editor credential and published only by the ones with the publisher credential. Listing 6-26 shows this example.

Listing 6-26 - Credentials Combination Syntax

editArticle:
  credentials: [ admin, editor ]              # admin AND editor

publishArticle:
  credentials: [ admin, publisher ]           # admin AND publisher

userManagement:
  credentials: [[ admin, superuser ]]         # admin OR superuser

Each time you add a new level of square brackets, the logic swaps between AND and OR. So you can create very complex credential combinations, such as this:

credentials: [[root, [supplier, [owner, quasiowner]], accounts]]
             # root OR (supplier AND (owner OR quasiowner)) OR accounts