I have controller with annotation * #Security("is_granted('PERM_MODULE_OUTBOUND_INVOICES_READ')") and I write test for some action in this controller, create user and loginIn, and when call rout for this action have error
Expression "is_granted('PERM_MODULE_OUTBOUND_INVOICES_READ')" denied access.
when add role to user PERM_MODULE_OUTBOUND_INVOICES_READ still have access denied
when commented tgis and in action check current user is granted have true
/**
* #Route("/manage/new_outbound_invoices", name="new_outbound_invoices")
*/
public function outBoundInvoiceListsAction(Request $request)
{
$check = $this->get('security.authorization_checker')
->isGranted('PERM_MODULE_OUTBOUND_INVOICES_READ', $this->getUser());
but with security annotation access denied why not understand
this is my test
$user = $this->user;
$this->logIn($user);
//$t = $this->getContainer()->get('security.context')->getToken(); try get token and have null, but in action have user from session
$this->client->setServerParameter('HTTP_HOST', 'erp.houseoptima.fi.local');
$crawler = $this->client->request('GET', '/economy/manage/new_outbound_invoices');
this function for LogIn
public function logIn(User $user)
{
$session = $this->client->getContainer()->get('session');
$firewall = 'main';
$token = new UsernamePasswordToken($user, null, $firewall, $user->getRoles());
$session->set('_security_'.$firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
What problem with this security ? With annotation error 403 withot 200 and when check in action is granted user have true
You need to pass the User object
/**
* #Security("is_granted('PERM_MODULE_OUTBOUND_INVOICES_READ', user)")
*/
public function indexAction(User $user)
{
Related
I am using Symfony Panther for some testing.
When I want to log a User like this :
protected function setUpPanther()
{
$client = static::createPantherClient(['readinessPath' => '/error']);
$client->manage()->window()->maximize();
$this->client = $client;
}
protected function loginPantherClient(Client $client, User $user)
{
$client->request('GET', '/error');
$session = self::$container->get('session');
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$session->set('_security_main', serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$client->getCookieJar()->set($cookie);
}
public function testUpdateProductDetails()
{
$this->setUpPanther();
$admin = $this->getSuperAdminAccount();
$this->loginPantherClient($this->client, $admin);
$this->client->request('GET', '/');
$this->assertSelectorExists('div.form-group');
}
A strange phenomenon occurs.
My client goes to the error page to initialize the cookie (required)
And here I have the impression that the authentication of my client is not taken into account, because if you look closely, in my test, I then make a request on
$this->client->request('GET', '/');
But instead of redirecting me to the requested page, it redirects me to / login, as if during the request, the user was not authenticated, and therefore, was redirected to the login page, while in my code, I authenticate the user before
Has someone already had this problem ?
Try to return client from loginPantherClient function:
protected function loginPantherClient(Client $client, User $user)
{
$client->request('GET', '/error');
$session = self::$container->get('session');
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$session->set('_security_main', serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$client->getCookieJar()->set($cookie);
return $client;
}
public function testUpdateProductDetails()
{
$this->setUpPanther();
$admin = $this->getSuperAdminAccount();
$this->client = $this->loginPantherClient($this->client, $admin);
$this->client->request('GET', '/');
$this->assertSelectorExists('div.form-group');
}
or remove from loginPantherClient function $client argument:
protected function loginPantherClient(User $user)
{
$this->client->request('GET', '/error');
$session = self::$container->get('session');
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$session->set('_security_main', serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
public function testUpdateProductDetails()
{
$this->setUpPanther();
$admin = $this->getSuperAdminAccount();
$this->loginPantherClient($admin);
$this->client->request('GET', '/');
$this->assertSelectorExists('div.form-group');
}
I am trying to create a route to programmatically assign a specific ROLE to current user. This is my attempt.
/**
* #Route("/role/assign/{role}", name="role_assignment")
*/
public function assign($role)
{
$session = $this->get('session');
$firewallContext = 'main';
$token = new UsernamePasswordToken(
'admin',
null,
$firewallContext,
array('ROLE_ADMIN')
);
$session->set('_security_'.$firewallContext, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$response = new JsonResponse([
'success' => 'true',
'user' => $this->getUser(),
]);
$response->headers->setCookie($cookie);
return $response;
}
User is always null, but I expected he become "admin" after page refresh.
I would strongly advice you against doing such things on production platforms. You will be better off properly configuring User Impersonation instead. It will save you the headache of having to manually do all of this.
If you really, really, want to go this way you could try the code below:
/**
* #Route("/role/assign/{username}/{role}", name="role_assignment")
*
* #param \Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface $tokenStorage
*
* #return JsonResponse
*/
public function assign($username, $role, TokenStorageInterface $tokenStorage)
{
// NOTES:
// 1. Make sure you are using the same User class as the one configured in `security.yml`
// 2. Keep in mind the $username MUST exist and MUST have the role you are setting,
// because the UserPasswordToken is reloaded from the session upon page refresh which triggers a check in the user provider and that will hit the database. In other words, if the user doesn't have `ROLE_ADMIN` you will most-likely get logged out or see "AccessDeniedException".
// For more information check \Symfony\Component\Security\Core\User\UserProviderInterface::refreshUser.
$user = new \Symfony\Component\Security\Core\User\User($username, null, array($role), true);
// Create token
$firewall = 'main'; // This MUST MATCH the name in your security.firewalls.->main<- or authentication WILL FAIL!
$usernamePasswordToken = new UsernamePasswordToken($user, null, $firewall, $user->getRoles());
// You don't need to save the token via $session->save().
// You can directly use $tokenStorage, which will do that for you.
$tokenStorage->setToken($usernamePasswordToken);
// Pass authentication to client.
return new JsonResponse(['success' => 'true', 'user' => $user]);
}
If you are trying to authenticate for test cases, you can have a look at my answer here which shows how you can configure a client which can authenticate as any user with any role you set (the user doesn't even have to exist in the db). This works fine for me on 3.4 so it should still work for 4.0.
I need to write a functional test in order to test that each role has the correct access to the pages.
In order to do that, I'm simulating authentication with a token, but I slightly edited the logIn method, just to call it with custom $username, $role and $firewall:
protected function logIn($username, $role, $firewall)
{
$session = $this->client->getContainer()->get('session');
$token = new UsernamePasswordToken($username, null, $firewall, $role);
$session->set('_security_' . $firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
So I am able to call it specifying which roles should have the fake user:
$this->logIn('my_username#example.com', ['ROLE_USER'], "my_firewall");
Then I can test if the user is not allowed or not to access certain routes:
// check if the access is correctly denied to the ROLE_USER
$this->client->request('GET', '/route-not-allowed-to-user');
$this->assertEquals(403, $this->client->getResponse()->getStatusCode());
// check if the access is correctly allowed to the ROLE_USER
$this->client->request('GET', '/route-allowed-to-user');
$this->assertNotEquals(403, $this->client->getResponse()->getStatusCode());
Those assertions work, the only problem is that in the view of the route-allowed-to-user I'm using twig to output the username:
{{ app.user.username }}
but it fails. I got status code 500 instead of getting 200, and the following error:
Impossible to access an attribute ("username") on a null variable ...
because app.user is not set.
How can I correctly set the user when simulating an authentication with token?
I think this happens because you didn't go through the authentication process and just created the user token which didn't trigger Symfony's event that store the user's username, roles and so on.
I did a similar thing recently by actually going through login form, filling data and sending it. Just like I was doing a real login attempt and it works well.
use Symfony\Component\DependencyInjection\ContainerInterface;
abstract class AuthenticatedTestCase extends KernelTestCase
{
static protected $client;
static public function setUpBeforeClass()
{
parent::setUpBeforeClass();
self::$client = static::$kernel->getContainer()->get('test.client');
}
static public function login($login, $password)
{
$crawler = self::$client->request('GET', '/test_admin/login');
$form = $crawler->filter('input[type="submit"]')->form([
'_username' => $login,
'_password' => $password,
]);
self::$client->submit($form);
// Redirect after successful login
self::assertEquals(302, self::$client->getResponse()->getStatusCode());
self::$client->followRedirect();
if (200 === self::$client->getResponse()->getStatusCode()) {
// Redirected URL is OK
// ...
}
}
}
I've resolved by editing the logIn method as follows:
protected function logIn($username, $password, $firewall)
{
$session = $this->client->getContainer()->get('session');
$authenticationManager = $this->client->getContainer()->get('security.authentication.manager');
$token = $authenticationManager->authenticate(
new UsernamePasswordToken(
$username,
$password,
$firewall
)
);
$session->set('_security_' . $firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
and using doctrine data fixtures in order to set users and roles.
I am attempting to log a user in programmatically in my functional test on SF 2.7 and FOSUserBundle dev-master. I have already found a good reference to log a user in via SO in this answer - Symfony2 - Tests with FOSUserBundle
The problem is that the second answer, logging the user in programmatically, doesn't work. Here is my code:
<?php
namespace Test\BackEnd\UserBundle\Controller;
use Test\Shared\CoreBundle\Tests\AbstractControllerTest;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\ORM\Tools\SchemaTool;
use FA\BackEnd\UserBundle\DataFixtures\ORM\LoadUserData;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
class DefaultController extends AbstractControllerTest
{
public function setUp()
{
$this->client = static::createClient();
$container = $this->client->getContainer();
$doctrine = $container->get('doctrine');
$em = $doctrine->getManager();
$schemaTool = new SchemaTool($em);
$metadata = $em->getMetaDataFactory()->getAllMetadata();
// Drop and recreate tables for all entities
$schemaTool->dropSchema($metadata);
$schemaTool->createSchema($metadata);
$loader = new Loader();
$user = new LoadUserData();
$user->setContainer($container);
$loader->addFixture($user);
$purger = new ORMPurger();
$executor = new ORMExecutor($em, $purger);
$executor->execute($loader->getFixtures());
$session = $container->get('session');
$userManager = $container->get('fos_user.user_manager');
$user = $userManager->findUserBy(array('username' => 'test'));
$firewall = 'default';
$token = new UsernamePasswordToken($user, $user->getPassword(), $firewall, $user->getRoles());
self::$kernel->getContainer()->get('security.token_storage')->setToken($token);
$session->set('_security_'.$firewall, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
public function testProfile()
{
//$this->createAuthorizedClient();
$token = $this->client->getContainer()->get('security.token_storage')->getToken();
$this->client->request('GET', '/profile/');
$this->assertEquals(
200,
$this->client->getResponse()->getStatusCode(),
"/profile isn't accessible"
);
}
}
Whenever I set a break point before the route gets executed, the token is return correctly:
Whenever I get to the function getUser() used by the Controller (http://api.symfony.com/2.7/Symfony/Bundle/FrameworkBundle/Controller/Controller.html#method_getUser) PHPStorm returns an empty token as viewed here:
So I decided to try the following code to log a user in, and it works.
$crawler = $this->client->request('GET', '/login');
$form = $crawler->selectButton('_submit')->form(array(
'_username' => 'test',
'_password' => 'test123',
));
$this->client->submit($form);
$this->client->followRedirect();
Am I not doing something properly whenever I log the user in programmatically? Is the session not being set properly?
Thanks!
Rat
I use this:
protected function createAuthorizedClient()
{
$client = static::createClient();
$container = $client->getContainer();
$session = $container->get('session');
$userManager = $container->get('fos_user.user_manager');
$loginManager = $container->get('fos_user.security.login_manager');
$firewallName = $container->getParameter('fos_user.firewall_name');
$user = $userManager->findUserBy(array('username' => 'USERNAME'));
$loginManager->loginUser($firewallName, $user);
// save the login token into the session and put it in a cookie
$container->get('session')->set('_security_' . $firewallName,
serialize($container->get('security.context')->getToken()));
$container->get('session')->save();
$client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
$this->client = $client;
}
and then in your test:
public function testMiInfo()
{
$this->createAuthorizedClient();
//else..
}
I try make test with authentication fosuserbundle, but still is fail, i find solution for symfony 2.3 but it doesn't works
https://gist.github.com/deltaepsilon/6391565
i also try create client by two funcitons
protected function createAuthorizedClient2()
{
$client = static::createClient();
$container = $client->getContainer();
$session = $container->get('session');
/** #var $userManager \FOS\UserBundle\Doctrine\UserManager */
$userManager = $container->get('fos_user.user_manager');
/** #var $loginManager \FOS\UserBundle\Security\LoginManager */
$loginManager = $container->get('fos_user.security.login_manager');
$firewallName = $container->getParameter('fos_user.firewall_name');
$user = $userManager->findUserBy(array('username' => 'admin'));
$loginManager->loginUser($firewallName, $user);
// save the login token into the session and put it in a cookie
$container->get('session')->set('_security_' . $firewallName, serialize($container->get('security.context')->getToken()));
$container->get('session')->save();
$client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
return $client;
}
protected function createAuthorizedClient()
{
$client = static::createClient();
$container = static::$kernel->getContainer();
$session = $container->get('session');
$person = self::$kernel->getContainer()->get('doctrine')->getRepository('BergUserDataBundle:UserLogin')->findOneByUsername('admin');
$token = new UsernamePasswordToken($person, null, 'main', $person->getRoles());
$session->set('_security_main', serialize($token));
$session->save();
$client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
return $client;
}
This is no longer the recommended way of testing with an authenticated client.
The new recommended way is much simpler - to submit plain old HTTP credentials and then tell your test environment firewall to authenticate via this method instead of the FOS User Provider.
See http://symfony.com/doc/current/cookbook/testing/http_authentication.html