10

I am developing a part of a web app build on Symfony 2. Like in many apps, authentication & authorization is required. How can I continue developing, taking considerations ACL by passing or faking a login?

In the docs, login_check does the authentication and sessions part transparently. I think I may either need to implement a version of that or somehow call it to login as different users/roles

Ondrej Slinták
  • 31,386
  • 20
  • 94
  • 126
Jiew Meng
  • 84,767
  • 185
  • 495
  • 805

3 Answers3

29

I'm not entirely sure I understand your question but are you just asking how you log in programatically?

In my tests I simply do a call like:

$this->container->get('security.context')->setToken(
    new UsernamePasswordToken(
        'maintenance', null, 'main', array('ROLE_FIXTURE_LOADER')
    )
);

In this instance 'maintenance' is not even a real User entity, its just a username that I made up for my fixtures so that they can access a service by having ROLE_FIXTURE_LOADER but if you did want to login as a full user entity (so that they have the correct ACL ID) you can get a $user object from the database and call:

$this->container->get('security.context')->setToken(
    new UsernamePasswordToken(
        $user, null, 'main', $user->getRoles())
    )
);

This doesn't do a full login but it does work with RBAC and I don't see why it wouldn't work with ACL if you pass an actual user object.

As for functional testing of my front end if I need to login I just navigate to the login page and submit the form as per the testing docs. For either of those to work you do need access to the container so you need to extend WebTestCase or roll your own ability to boot the kernel (see here).

I've a feeling I've mis-understood the question though (i.e. you need to do something more complex than just placing the token). Perhaps you could try to clarify a bit more what you mean by

passing or faking a login

A concrete example to plant a security token in a test:

First we make a base class for our tests to use which contain a login convenience method. This can be done by extending WebTestCase and using the getContainer method on a client OR you can pull WebTestCase apart to roll your own base class which just boots the kernel without a client and returns the container (see my link for two solutions to achieve that).

namespace Acme\SomeBundle\Tests;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;

abstract class AcmeTestCase extends WebTestCase {

    protected function loginAs($client, $username) {
        $container = $client->getContainer();
        $doctrine = $container->get('doctrine');

        $user = $this->loadUser($doctrine, $username);

        // First Parameter is the actual user object.
        // Change 'main' to whatever your firewall is called in security.yml
        $container->get('security.context')->setToken(
            new UsernamePasswordToken(
                $user, null, 'main', $user->getRoles()
            )
        );
    }

    private function loadUser($doctrine, $username) {
        // Don't have to use doctrine if you don't want to, you could use
        // a service to load your user since you have access to the
        // container.

        // Assumes User entity implements UserInterface
        return $doctrine
                ->getRepository('AcmeUserBundle:User')
                ->findOneByUsername($username);
    }

}

Then you just need to use your base class in any test you wish. Like so:

namespace Acme\SomeBundle\Tests\Entity;

use Acme\SomeBundle\Tests\AcmeTestCase;

class SomeEntityTest extends AcmeTestCase {

    public function somethingTest() {
        $this->loginAs(static::createClient(), 'SomeUsernameInDB');

        // Do the rest of your test here.
    }

}

Hopefully this helps.

Community
  • 1
  • 1
Kasheen
  • 5,401
  • 2
  • 31
  • 41
  • I think u got it right, I can try that later. I just want to fake a login so that I can test out my ACL logic. – Jiew Meng Dec 13 '11 at 00:47
  • Hopefully it will help you then :). To test out ACL use my second snippet. Just get your `$user` entity from the `EntityManager` or `Repository` first and pass it in to the token as `$user`. – Kasheen Dec 13 '11 at 10:24
  • Ok, just tested this actualy ... I am getting `There is no user provider for user "Bottle\Bundle\NotesBundle\Entity\User"` now . I actually created a custom `FakeUserToken` maybe I shld just use the UsernamePasswordToken? I was trying to be as close as possible to the real thing where I can get the loggedin user. How can I do that with `UsernamePasswordToken`? – Jiew Meng Dec 16 '11 at 07:23
  • Hi, `UsernamePasswordToken` is what you should use, it can be passed a real user object that implements `UserInterface` - this is the token that Symfony2 uses for Form Login as far as I know. I need to go out, but when I get back I'll update the answer with a more complete solution which might help. – Kasheen Dec 16 '11 at 11:00
  • I've posted some more code which will hopefully help, post back if it doesn't and perhaps there will be another solution. – Kasheen Dec 16 '11 at 18:02
  • It's `security.token_storage` instead of `security.context` in recent versions of symfony. – fracz Mar 17 '17 at 10:56
1

Not sure I understand your question very well but If you just want to see the application with different user role you can use the Symfony Role Switcher documented here : http://symfony.com/doc/current/book/security.html#impersonating-a-user

So you just have to put a parameter in your url to view your app as another connected user.

Hope it helps !

KaipiYann
  • 21
  • 2
0

Check out: https://github.com/schmittjoh/JMSSecurityExtraBundle/tree/master/Tests/Functional

idunnolol
  • 33
  • 4
  • I think perhaps he is meaning for you to extend `BaseTestCase` from that bundle and calling the login method, I'm not entirely sure though. – Kasheen Dec 12 '11 at 18:55