Symfony2, FOSUserBundle, authentication with cookies disabled - symfony

How I can authentication in Symfony2 without cookies in a brouser? How can generate some like this http://some.site/hello/roman?PHPSESSID=9ebca8bd62c830d3e79272b4f585ff8f or this http://some.site/9ebca8bd62c830d3e79272b4f585ff8f/hello/roman or some other url that was always available sessionid parameter. Thank you for any help.

You have to to two things. First you must extend the session storage to get the session from the query param.
namespace Elao\BackBundle\Session;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Session\Storage\NativeFileSessionStorage;
class Storage extends NativeSessionStorage
{
public function __construct($savePath = null, array $options = array(), ContainerInterface $container)
{
$request = $container->get('request');
if ($request->query->has('sessionId')) {
$request->cookies->set(session_name(), 1); // We have to simulate this cookie, in order to bypass the "hasPreviousSession" security check
session_id($request->query->get('sessionId'));
}
return parent::__construct($savePath, $options);
}
}
Source: http://www.elao.com/blog/symfony-2/symfony-2-loading-session-from-query-param.html
The next point, should be replacing the UrlGenerator to generate every url with the session id param. A example to do this, can be found in this answer.
But as nifr in the comment said, it's not a very clean requirement.

Related

How I can mock `Dingo\Api\Auth\Provider\JWT` so I can bypass the Authentication overhwad whilst I am unit testing my endpoints?

I am using dingo/api in my api and I want to unit test the endpoint:
class MyApiTest extends TestCase
{
public function testEndpoint()
{
$dispatcher = app('Dingo\Api\Dispatcher');
$fake_token = 'cndksjonsdcnsod';
$dispatcher->header('Authorization', 'Bearer: '.$fake_token);
$dispatcher->version($version)->get('/my-endpoint');
}
}
In my app.php I have the following configuration:
'auth' => [
'jwt' => Dingo\Api\Auth\Provider\JWT::class,
],
Is there a way to mock/fake/set default values to the Dingo\Api\Auth\Provider\JWT provider of jwt authentication?
An approach that worked for me, is via testing the controller itself and mock JWT authentication service by bypassing the Dingo and any middleware used by routing itself.
Example:
Let us suppose we have the following controller:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Tymon\JWTAuth\Facades\JWTAuth;
class ProfileController extends Controller
{
public function getProfile(Request $request,$profile_id)
{
$user = JWTAuth::parseToken()->authenticate();
$language = App::getLocale();
// Do stuff Here
}
}
You can write a simple test:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Tymon\JWTAuth\Facades\JWTAuth;
// Set a test class for speed is ommited
public function testMyApiCall()
{
/**
* $user is an instance of a User
*/
JWTAuth::shouldReceive('parseToken->authenticate')->andReturn($user);
App::setlocale('el');
$request = new Request();
$request->initialize([],['token' => 'AAABBBCCC' ],[],[],[],[],[]);
$controller = new ProfileController();
// I ommit the profile_id value it is just a demonstration
$response = $controller->getProfile($request,$profile_id)
$response_dody = $response->getData(false);
// Perform assertions upon $response_dody
}
In our case we do not care about what routing is used and how it is set up. Therefore, are no mentioning any routing and anything regarding Dingo in this example, we just forget it.
Cons and pros
Though it is not a silver bullet, it is an approach that will give a reliable result focusing on the actual code. Keep in mind though that you bypass many middlewares that may you also want to test as well eg. Authentication ones.
On the other hand you are able to test the logic inside the controller, in cases where the logic is rather small to create a seperate class/method for it eg. selecting data from DB.

What is the best way to create a singleton entity in Symfony 4?

I want to create a settings page, which only has a form in it. If the form is submitted it only updates settings entity but never creates another one. Currently, I achieved this like:
/**
* #param SettingsRepository $settingsRepository
* #return Settings
*/
public function getEntity(SettingsRepository $settingsRepository): Settings
{
$settings = $settingsRepository->find(1);
if($settings == null)
{
$settings = new Settings();
}
return $settings;
}
In SettingsController I call getEntity() method which returns new Settings entity (if the setting were not set yet) or already existing Settings entity (if setting were set at least once).
However my solution is quite ugly and it has hardcoded entity id "1", so I'm looking for a better solution.
Settings controller:
public function index(
Request $request,
SettingsRepository $settingsRepository,
FlashBagInterface $flashBag,
TranslatorInterface $translator,
SettingsService $settingsService
): Response
{
// getEntity() method above
$settings = $settingsService->getEntity($settingsRepository);
$settingsForm = $this->createForm(SettingsType::class, $settings);
$settingsForm->handleRequest($request);
if ($settingsForm->isSubmitted() && $settingsForm->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($settings);
$em->flush();
return $this->redirectToRoute('app_admin_settings_index');
}
return $this->render(
'admin/settings/index.html.twig',
[
'settings_form' => $settingsForm->createView(),
]
);
}
You could use Doctrine Embeddables here.
Settings, strictly speaking, should not be mapped to entities, since they are not identifiable, nor meant to be. That is, of course, a matter of debate. Really, a Settings object is more of a value object than an entity. Read here for more info.
So, in cases like these better than having a one to one relationship and all that fuzz, you probably will be fine with a simple Value Object called settings, that will be mapped to the database as a Doctrine Embeddable.
You can make this object a singleton by creating instances of it only in factory methods, making the constructor private, preventing cloning and all that. Usually, it is enough only making it immutable, meaning, no behavior can alter it's state. If you need to mutate it, then the method responsible for that should create a new instance of it.
You can have a a method like this Settings::createFromArray() and antoher called Settings::createDefaults() that you will use when you new up an entity: always default config.
Then, the setSettings method on your entity receieves only a settings object as an argument.
If you don't like inmutablity, you can also make setter methods for the Settings object.

Disable CSRF protection in form generated by Sonata Admin

In Sonata Admin I need to disable the CSRF token in some of my forms but sometimes I don't want to create a Form Type class, choosing instead to let Sonata generate the form, as such:
/** #var $form \Symfony\Component\Form\Form */
$form = $this->admin->getForm();
How can I disable the CSRF token from this point?
Without a Form Type class, the best way to change the CSRF field would be in the admin Class. For that, it's possible to override this function:
public function getFormBuilder() {
$this->formOptions['data_class'] = $this->getClass();
$this->formOptions['csrf_protection'] = false;
$formBuilder = $this->getFormContractor()->getFormBuilder(
$this->getUniqid(),
$this->formOptions
);
$this->defineFormBuilder($formBuilder);
return $formBuilder;
}
I think it's not possible as from \Symfony\Component\Form\Form instance you have access to object ($form->getConfig()) that implements FormConfigInterface: you can call on it getOptions or getOption but there is no setOption method.

CSRF Token from the controller

I have a controller getting a form posted.
public function myPostAction(Request $request)
{
$form = $this->createForm('my_form', $my_object);
$form->handleRequest($request);
#...
I can see my CSRF token posted as parameter
my_form[_token] => lH38HTm5P0Cv3TOc4-9xi2COx-cZ670mpJ_36gR8ccI
I simply need to read it
$form->get('_token')
This tells me
Child "_token" does not exist.
How can I get this token ?
Here is the workaround I'm going to use meanwhile:
$token = $request->get($form->getName())['_token'];
I also noticed by chance that the intention used to generate the token is the form name
$csrf = $this->get('form.csrf_provider');
$intention = $form->getName();
$token = $csrf->generateCsrfToken($intention);
Like #Pierre de LESPINAY said, it is possible to do it by retrieving Token Manager service.
This service can also be injected in your constructor like that :
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
...
public function __construct(CsrfTokenManagerInterface $tokenManager)
{
$this->tokenManager = $tokenManager;
}
And used later like previously demonstrated :
$token = $this->tokenManager->getToken('myformname')->getValue();
You can get it with:
$request->request->get('my_form[_token]');
If you didn't disable CSRF-protection it will be applied and validated automatically and you don't need to check it by self.

Symfony2: How to get user Object inside controller when using FOSUserBundle?

I'm using FOSUserBundle to authenticate my users.
I'm trying to get the user object inside the Controller to register a trip where I should add the user object to this Trip before save.
I did not found how to do that because next method where I found it in symfony doc:
$user = $this->container->get('security.context')->getToken()->getUser();
renders the username as string, but I need the whole object.
Currently, I use this method, but it's not working properly.
$username = $this->container->get('security.context')->getToken()->getUser();
$em = $this->container->get('doctrine')->getEntityManager();
$user = $em->getRepository('SiteUtilisateurBundle:Utilisateur')->find($username);
How can I correctly do this?
I think Ramon is right. You already have the user object.
Also in Symfony > 2.1.x you can use
$this->getUser();
inside the controller.
The documentation for the getUser method indicates:
either returns an object which implements __toString(), or a primitive string is returned.
And if we look in the FOS\UserBundle\Model\User class over here (the base user class used by the FOSUserBundle) we can see that it does indeed have a __toString method:
public function __toString()
{
return (string) $this->getUsername();
}
I think that you actually get the User object but because it implements a __toString method it can be rendered directly in templates.
In Twig you can use:
{{ dump(user) }}
To see what kind of object you have. But You are actually using an object, not a string.
Solution:
$userManager = $this->container->get('fos_user.user_manager');
$user = $userManager->findUserByUsername($this->container->get('security.context')
->getToken()
->getUser())
In FOSUser 1.3 you can't call directly $this->getUser in SecurityController.
You have to call $this->container->get('security.context')->getToken()->getUser();
And this is enough to access the user object.
No need to call $user = $em->getRepository('SiteUtilisateurBundle:Utilisateur')->find($username);
Furthermore your find method automatically and implicitly cast your initial $username object to string because it doesn't wait an object as argument.
I had the same issue, to resolve it add the FOS classes in your use section i.e:
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\Model\UserInterface;
In symfony >= 3.2, documentation states that:
An alternative way to get the current user in a controller is to
type-hint the controller argument with UserInterface (and default it
to null if being logged-in is optional):
use Symfony\Component\Security\Core\User\UserInterface\UserInterface;
public function indexAction(UserInterface $user = null)
{
// $user is null when not logged-in or anon.
}
This is only recommended for experienced developers who don't extend
from the Symfony base controller and don't use the ControllerTrait
either. Otherwise, it's recommended to keep using the getUser()
shortcut.
Here is blog post about it
For FOSUser ^1.3 you can get current user from inside a controller that extends BaseController like this :
$user = $this->container->get('security.token_storage')->getToken()->getUser();
public function indexAction()
{
/* #var $user \FOS\UserBundle\Model\UserInterface */
if ($user = $this->getUser())
{
echo '<pre>';
print_r($user);
print_r($user->getRoles()); // method usage example
exit;
return $this->redirectToRoute('dashboard');
}
return $this->redirectToRoute('login');
}

Resources