Ramon Kleiss
Ramon Kleiss

Reputation: 1749

Symfony2 - Secure specific HTTP methods for URL

So, I'm trying to make a RESTful API in Symfony2, but I'm having an issue with the security.

Let's say, for example, I want to create a new user with my API. I will do the following request:

POST /api/v1/users.json HTTP/1.1

This URL should be accesible by all clients, so there's no authentication required. But, let's say I want to request a list of all the users. According to the REST idea, I should make a GET request:

GET /api/v1/users.json HTTP/1.1

Of course, I don't want this list to be accesible by everyone, so I'll have to secure it in Symfony2. The following won't work, of course, since it secures the entire URL pattern, and not the HTTP method:

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

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    providers:
        in_memory:
            users:
                user:  { password: userpass, roles: [ 'ROLE_USER' ] }
                admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }

    firewalls:
        secured_area:
            pattern:    ^/
            anonymous: ~
            http_basic:
                realm: "Social Portal API"

    access_control:
        - { path: /api/v1/users.json, roles: ROLE_ADMIN }

So, is there a secret parameter for the access_control directive that secures the HTTP method? Or is there any other way? I've tried to use the JMSSecurityExtraBundle:

/**
 * @Secure(roles="ROLE_ADMIN")
 */
public function listAction()
{
    return new Response('Cubilon\\SocialPortal\\APIBundle\\Controller\\UserController', 200);
}

Which should secure this method, but it didn't work...

How can I secure a certain HTTP method in combination with a URL pattern?

EDIT:

So, as I said under the answer below, I've fixed it using the JMSSecurityExtraBundle. I've defined the services I want to secure in Resources/config/services.xml:

# Resources/config/services.xml
<?xml version="1.0" encoding="utf-8"?>
<services>
    <service id="user_controller" class="Cubilon\SocialPortal\APIBundle\Controller\UserController">
        <tag name="security.secure_service" />
    </service>
</services>

And I then secured each action accordingly in the UserController:

# Controller/UserController.php
<?php
namespace Cubilon\SocialPortal\APIBundle\Controller\UserController;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use JMS\SecurityExtraBundle\Annotation\Secure;

class UserController extends Controller
{
    public function createAction($_format)
    {
        // ...
    }

    /**
     * @Secure(roles="ROLE_USER, ROLE_ADMIN")
     */
    public function readAction($username, $_format)
    {
        // ...
    }

    /**
     * @Secure(roles="ROLE_USER, ROLE_ADMIN")
     */
    public function updateAction($username, $_format)
    {
        // ...
    }

    /**
     * @Secure(roles="ROLE_USER, ROLE_ADMIN")
     */
    public function deleteAction($username, $_format)
    {
        // ...
    }
}

In each secured action I check the credentials of the secured user (whether the authenticated username is the same as the requested username and such).

Upvotes: 11

Views: 4284

Answers (3)

Nuno Costa
Nuno Costa

Reputation: 1620

I know it is late, but if someone stumbles on this questions here is how to secure per a request per HTTP method (see the Symfony security documentation):

# app/config/security.yml
security:
    # ...
    access_control:
        - { path: ^/api/v1/users.json, roles: ROLE_ADMIN, methods: [POST, PUT] }
        - { path: /api/v1/users.json, roles: ROLE_ADMIN }

Be careful the order in which you set the rules matters.

Upvotes: 12

Matthieu Napoli
Matthieu Napoli

Reputation: 49553

Watch out, there are 2 things:

  • firewall (authentication)
  • access control (authorization)

The accepted answer shows how to restrict an access control rule to an HTTP method, but here is how to restrict a firewall rule to an HTTP method:

security:
    firewalls:
        secured_area:
            methods: [POST, PUT]

Note that this feature was added in Symfony 2.5.

As shown in the other answer, here is how to restrict an access control rule to an HTTP method:

security:
    # ...
    access_control:
        - { path: ^/api/v1/users.json, roles: ROLE_ADMIN, methods: [POST, PUT] }

Upvotes: 5

Sybio
Sybio

Reputation: 8645

According to the security reference book, you can't secure a URL by method.

Not the best way but you can do like that, in the action:

public function listAction(Request $request)
{
    if ($request->getMethod() == 'GET' && !$this->get('security.context')->isGranted('ROLE_ADMINISTRATOR')) {
        throw $this->createNotFoundException("This page doesn't exist."); // forward a 404, or a message in a json...
    }

    return new Response('Cubilon\\SocialPortal\\APIBundle\\Controller\\UserController', 200);
}

Or you can create a new kernel event listener that will check the method and the user ROLE like my previous example, but extend to all the actions ! ^^

Upvotes: 4

Related Questions