Phpunit token storage error during test - php

I have a test file to test services instantiation and i have made a custom menu with KnpMenuBundle.
Everything is working expect phpunit who return an error when testing my MenuBuilder.
There is the function who test all services instantiation my test file :
class ServiceAvailabilityTest extends KernelTestCase
{
/**
* #dataProvider getServiceIds
*
* #param $serviceId
*/
public function testServiceInstance($serviceId)
{
static::bootKernel();
static::$kernel->getContainer()->get($serviceId);
}
}
On my MenuBuilder i use authorizationChecker to know if the user is granted or not, like this.
if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
$menu->addChild('sidebar.front.administration', ['route' => 'sonata_admin_redirect'])
->setExtra('translation_domain', 'navigation')
->setAttribute('icon', 'fa fa-eye');
}
When i'm removing all this, tests are ok
$this->authorizationChecker->isGranted('ROLE_ADMIN')
There is the error i get when i run phpunit
1) Tests\ServiceAvailabilityTest::testServiceInstance with data set #423 ('menu.main')
Symfony\Component\Security\Core\ExceptionAuthenticationCredentialsNotFoundException: The token storage contains no authentication token. One possible reason may be that there is no firewall configured for this URL. /code/vendor/symfony/symfony/src/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.php:57
/code/src/AppBundle/Menu/MenuBuilder.php:192
/code/src/AppBundle/Menu/MenuBuilder.php:101
/code/app/cache/test/appTestDebugProjectContainer.php:8311
/code/vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php:312
/code/tests/ServiceAvailabilityTest.php:3
There is my menu services if you have to check them
menu.builder:
class: AppBundle\Menu\MenuBuilder
arguments: [ '#knp_menu.factory', '#doctrine', '#manager.server','#security.authorization_checker', '#request_stack' ]
menu.main:
class: Knp\Menu\MenuItem
factory: [ '#menu.builder', 'createMainMenu' ]
arguments: [ '#request_stack' ]
tags:
- { name: knp_menu.menu, alias: sidebar }
I already search on the internet but they fix this by adding an access control on security.yml like this
- { path: ^/, role: ROLE_USER }
But i don't have any route for a menu.
Does someone already had this phpunit error ?
Thanks,

Try this:
/**
* #param string $firewallName
* #param UserInterface $user
* #param array $options
* #param array $server
*/
protected function loginUser($firewallName, UserInterface $user, array $options = array(), array $server = array())
{
$this->client = static::createClient();
$token = new UsernamePasswordToken($user, null, $firewallName, $user->getRoles());
static::$kernel->getContainer()->get('security.token_storage')->setToken($token);
// <2.8 this may be usefull
//$request = new Request();
//$event = new InteractiveLoginEvent($request, $token);
//static::$kernel->getContainer()->get('event_dispatcher')->dispatch('security.interactive_login', $event);
$session = $this->client->getContainer()->get('session');
$session->set('_security_'.$firewallName, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}
in your testCase/setUp for example::
static::bootKernel();
$this->loginUser('admin', $testUser);
$this->assertNotFalse(static::$kernel->getContainer()->get('security.authorization_checker')->isGranted('ROLE_ADMIN'));

Related

Adding public data to the JWT response with lexik

I am currently using Symfony 5 with lexik and when I to generate the JWT token, I would like for the response to get me the token and the username so I could have something like this:
{
"username":"username"
"token": "token"
}
I tried of course to use the doc here : https://github.com/lexik/LexikJWTAuthenticationBundle/blob/master/Resources/doc/2-data-customization.md#eventsauthentication_success---adding-public-data-to-the-jwt-response but whenever I test it in Postman, I still only get the token and I am completely stuck on what to do...
My EventListener:
<?php
namespace App\EventListener;
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent;
class AuthenticationSuccessListener
{
/**
* #param AuthenticationSuccessEvent $event
*/
public function onAuthenticationSuccessResponse(AuthenticationSuccessEvent $event)
{
$data = $event->getData();
$user = $event->getUser();
if (!$user instanceof UserInterface) {
return;
}
$data['data'] = array(
'username' => $user->getUsername(),
);
$event->setData($data);
}
}
the service in services.yaml
acme_api.event.authentication_success_listener:
class: App\EventListener\AuthenticationSuccessListener
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.handler.authentication_success, method: onAuthenticationSuccessResponse }
Any help would be greatly appreciated
Try to add the user interface use inside your Listener it might be that you are not getting throw your if statment because of this:
use Symfony\Component\Security\Core\User\UserInterface;

Pass render to my new created service - Symfony

Please, I am new to symfony, I can't seem to make this work. I created a simple service that will check if the user status is active or deleted, and if they are, log them out from application. I have created the code that works, and then I wanted to be a badass and create a service that will do this. But nooo. So, I created a service and it prints out the result, so it is registered and it is working. The problem is I guess that I cannot approach the same variables as I did in Controllers to my service class. Here is the code:
<?php
namespace WebBundle\Roles;
class Roles
{
public function getApplicationId($loggedUser, $request)
{
// Get the current user role
/** #var $userRole array */
$userRole = $loggedUser->getRoles();
// Check if the user role is super admin or admin - client
/** #var $superAdmin bool */
$superAdmin = in_array('ROLE_SUPER_ADMIN', $userRole) ? true : false;
$admin = in_array('ROLE_ADMIN', $userRole) ? true : false;
if ($superAdmin) {
/** #var $application int */
$application = $request->get('application');
} elseif ($admin) {
/** #var $application int */
$application = $loggedUser->getAppClient()->getId();
}
return $application;
}
public function logoutInactiveAndDeletedUsers($loggedUser)
{
// Log out inactive or deleted users.
if ($loggedUser->getStatus()->getStatus() == 'inactive' || $loggedUser->getStatus()->getStatus() == 'deleted' ) {
//$this->get('security.context')->setToken(null);
//var_dump('test');exit;
return $this->container->render('WebBundle:Default:login.html.twig', array('last_username' => null, 'error' => null,));
}
}
}
So, this first service getApplicationId is working fine. But the other one is causing me real trouble. So the serivce is breaking when I call both:
$this->get('security.context')->setToken(null);
and
return $this->container->render('WebBundle:Default:login.html.twig', array('last_username' => null, 'error' => null,));
But If I put this code in Controller, it works perfectly. It destroys the token and redirect the user to the login page. Please, help me understand how to make it work as a service to.
Ok, I figure out how to call a view:
In services.yaml add templating as an argument
arguments: [#templating]
Then create a property and assign it to a constructor:
private $templating;
public function __construct($templating)
{
$this->templating = $templating;
}
And call it like $this:
$this->templating->render('WebBundle:Default:login.html.twig', array('last_username' => null, 'error' => null,));
Now I need to find a solution how to disable token for the user. If anybody know help me out dude (I am not stoner anymore).

Symfony Custom UserProvider Auth + Parse SDK

Heyo!
I know it's a common problem people having problems with custom providers and web service authentication. I'm spending hours trying to figure out how to do that but I'm almost freaking out.
So, the thing is: I'm using the Symfony Firewalls with a custom UserProvider and a AbstractGuardAuthenticator as well. The problem is in the loadUserByUsername($username) function inside the UserProvider implementation.
For security reasons I can't retrieve the user password from Parse (my web service), and the loadUserByUsername($username) function asks for that. Even in the documentation about how to create a custom user provider using web services they are retrieving the user password from the database.
So what's the solution in that case? What can I do when I don't have access to the user password?
My current code is something like that:
$app['app.authenticator'] = function () {
return new Authenticator($app);
};
$app['security.firewalls'] = array(
'login' => array(
'pattern' => '^/login/$',
),
'secured' => array(
'pattern' => '^.*$',
'form' => array('login_path' => '/login/', 'check_path' => '/login/auth/'),
'logout' => array('logout_path' => '/logout/', 'invalidate_session' => true),
'guard' => array(
'authenticators' => array(
'app.authenticator'
),
),
'users' => function () use ($app) {
return new UserProvider($app);
},
)
);
The Authenticator.php is quite big code because extends the AbstractGuardAuthenticator class. But I'm basically using this one from Symfony docs. The only thing Is that I'm sending to the UserProvider class the username AND the password as well, because that way I can check if the user and password are right. Like this:
public function getUser($credentials, UserProviderInterface $userProvider) {
return $userProvider->loadUserByUsername($credentials);
}
And my UserProvider class is the default one, I'm just checking inside the loadUserByUsername function if the credentials comming from my Authenticator are right. Something like this:
public function loadUserByUsername($credentials) {
$encoder = new BCryptPasswordEncoder(13);
try {
$user = ParseUser::logIn($credentials['username'], $credentials['password']);
} catch (ParseException $error) {
throw new UsernameNotFoundException(sprintf('Invalid Credentials.'));
}
return new User($credentials['username'], $encoder->encodePassword($credentials['password'], ''), explode(',', 'ROLE_USER'), true, true, true, true);
}
The problem is: after the login (everything with the login is working fine), Silex calls the loadUserByUsername function in every page which needs to be secured, but just sending the username parameter.
So basically, I don't know what to do guys. I'm really trying to figure out how to get this thing working.
Thanks for your help!
I have a similar implementation and this issue is well known. In my user provider I have the methods loadUserByUsername($username) and refreshUser(UserInterface $user). Since I have the same issue like you, I don't check the user in loadUserByUsername but simple return a new Object with only the username in it to not disturb the flow. loadUserByUsername doesn't make sense for external APIs, so I simply jump over it. The method refreshUser is either beeing called on every request, this is usefull.
In your AbstractGuardAuthenticator you have the method createAuthenticatedToken, which returns an token. There you should have the full authentificated user:
abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface
{
/**
* Shortcut to create a PostAuthenticationGuardToken for you, if you don't really
* care about which authenticated token you're using.
*
* #param UserInterface $user
* #param string $providerKey
*
* #return PostAuthenticationGuardToken
*/
public function createAuthenticatedToken(UserInterface $user, $providerKey)
{
//do login stuff
//save password in user
return new PostAuthenticationGuardToken(
$user,
$providerKey,
$user->getRoles()
);
}
}
Then, I would't use loadUserByUsername but refreshUser instead. Both are called on every request:
/**
* Don't use
* #codeCoverageIgnore
* #param string $username
* #return User
*/
public function loadUserByUsername($username)
{
return new User($username, null, '', ['ROLE_USER'], '');
}
/**
* Refresh user on every subrequest after login
* #param UserInterface $user
* #return User
*/
public function refreshUser(UserInterface $user)
{
$password = $user->getPassword();
$username = $user->getUsername();
//login check
}

Controller not found Symfony

I am a beginner in Symfony 2.8. I have a problem with my controller.
That is my controller:
class ExampleController extends ExtraController
{
/**
* #ParamConverter("site", class="Bundle:Site", converter="site_slug_converter")
* #Route("/formacion-example", name="example_web.front.example_training", requirements={"site": "es"})
*
* Render the Example form page
*
* #param Site $site
*
* #return Response
*/
public function example2TrainingFormAction(Site $site)
{
$options = ['site' => $site, 'projectId' => $this->get('example.doctrine.project_getter')->getProject()];
$form = $this->createForm(ExampleTrainingType::class, null, $options);
$viewData = ['form' => $form->createView()];
return $this->render('ExampleFrontContactFormBundle:Example:example_training.html.twig', $viewData);
}
}
When I go to my Route www.example.com/es/formacion-example symfony return to me:
HTTP status: Error 500
Controller: n/a
Route name:example_web.front.example_training
Has session: no
In symfony documentation I cant find a solution.
Thank you! :)
adding the answer here as well:
i.e. the site parameter was missing from the route
#Route("/{site}/formacion-example", ...

How to serialize a user regarding authentication?

I'm have an authenticated section on my app, but the authentication is done via oauth to a 3rd party service. I get the 200 callback from the service, now I create/find my user and set him as logged in.
So my provider is:
providers:
users:
entity: { class: MainBundle:User, property: id }
My user implements the security's UserInterface, although I don't have the username, password, etc properties on my user entity. I assume the id is then the only identifier I can use then.
My token is set as follows:
$token = new UsernamePasswordToken($user, null, 'secured_area', $user->getRoles());
$this->securityContext->setToken($token);
I wish to do this without using JMS bundle for now; my serialization looks like this:
/**
* Serialize
* #return string|void
*/
public function serialize()
{
return serialize(array(
'id' => $this->getId(),
'display_name' => $this->getDisplayName(),
'email' => $this->getEmail(),
));
}
/**
* Unserialize
* #return mixed|void
*/
public function unserialize($data)
{
$data = unserialize($data);
$this->setId($data['id']);
$this->setDisplayName($data['display_name']);
$this->setEmail($data['email']);
return $this;
}
Using the above I get an infinite loop redirect.
At first I only serialized the id, but then all the other properties of the user isn't available.
Then I tried serializing the whole object ($this), but that gives me a xdebug nesting level error of 1000.
I'm a bit lost of how to make this authentication work with serialization
security.yml
providers:
users:
entity: { class: MainBundle:User, property: id }
login logic
$token = new UsernamePasswordToken($user, null, 'secured_area', $user->getRoles());
$this->securityContext->setToken($token);
I took out the serialization out of the user entity, also not implementing equatable interface. Also still do not have the interface properties, although this is needed:
public function getUsername()
{
return $this->getId();
}

Categories