Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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).

like image 540
Ramon Kleiss Avatar asked Jan 25 '12 15:01

Ramon Kleiss


2 Answers

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.

like image 120
Nuno Costa Avatar answered Oct 26 '22 06:10

Nuno Costa


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 ! ^^

like image 31
Sybio Avatar answered Oct 26 '22 06:10

Sybio