Disable CSRF protection in form generated by Sonata Admin - symfony

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.

Related

Send a custom email from sonata admin using variables

I have a list view in sonata admin. I want to add a column that will allow me to click on a link to send an email. The link action will know variables from that row in the table so that it can fill in the email. I was able to add the column and can visualize a twig template. I've added the following function to the Admin Class:
public function sendEmail( \Swift_Mailer $mailer)
{
$message = (new \Swift_Message('some email'))
->setFrom('contact#example.com')
->setTo('contact#example.com')
->setBody(
$this->renderView(
'emails/response.html.twig',
array('manufacturer' => $manuvalue, 'modelnum' => $modelnumvalue, 'email' => $email)
),
'text/html');
$mailer->send($message);
}
I'm stuck on how to connect these pieces together so that when I click on the link the email is sent and includes the params from the row. I have email working on form submit in other areas of the site, but need help figuring out the way to do this manually.
As you mentioned in the comments, what you want to do is typically a Custom Action
In order to ensure that this action can not be accessed via direct request and can only be performed by admin, you could do use a template like this for your customAction :
...
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
...
//AuthorizationCheckerInterface injected as authorizationChecker
public function customAction($id){
$object = $this->admin->getSubject();
if (!$object) {
throw new NotFoundHttpException(sprintf('unable to find the object with id: %s', $id));
}
// Check access with role of your choice
$access = $this->authorizationChecker->isGranted('ROLE_ADMIN');
if($access){
//Do your action
} else {
throw $this->createAccessDeniedException("You don't have the rights to do that!");
}
}
I ended up doing a custom route and protected it with security settings that #dirk mentioned.

Tow Registration form FosUserBundle Symfony

I work with symfony 2.8 and FOSUserBundle, I have two type of user in the same table in database , And I like to differenciate the registration form in the same page of registration like this :
*
the problem that I can't use two instance of the form in the same page, what can I do please?
The way I would go about this, is override FOSUserBundle and then extend the RegistrationController and likely the corresponding template.
In the registerAction you can reuse some parts of the original, but where the form is created you then create two different ones, maybe like this:
/** #var $formFactory FactoryInterface */
$clientFormFactory = $this->get('client_registration.form.factory');
$clientForm = $clientFormFactory->createForm();
$clientForm->setData($client);
/** #var $formFactory FactoryInterface */
$correspondentFormFactory = $this->get('correspondent_registration.form.factory');
$correspondentForm = $correspondentFormFactory->createForm();
$correspondentForm->setData($correspondent);
$clientForm->handleRequest($request);
$correspondentForm->handleRequest($request);
if ($clientForm->isSubmitted() && $clientForm->isValid()) {
// ...
} elseif ($correspondentForm->isSubmitted() && $correspondentForm->isValid()) {
// ...
}
return $this->render(
'#FOSUser/Registration/register.html.twig',
[
'clientForm' => $clientForm->createView(),
'correspondentForm' => $correspondentForm->createView(),
]
);
The part inside the if conditions will then probably look similar as to the original controller. You might have different UserManager's for each user type, you have to switch out, but other than that it's basically: dispatch pre-event, save user, dispatch post-event, redirect. It is important that you dispatch both events as other parts of FOSUserBundle will rely on them, e.g. sending a registration email.
In your template you then just render both forms in their tab. You might have to fiddle around with the form id's a bit, but that should be straightforward.

Sonata Admin - how to set the menu.label attribute?

According to the Sonata source code, the last node in the breadcrumb is rendered this way:
# standard_layout.html.twig #
<li class="active"><span>{{ menu.label }}</span></li>
In my setup, when opening a given Admin subclass, the last node simply becomes a raw string according to the entity handled by the Admin:
Dashboard / Entity List / Acme\SomeBundle\Entity\Stuff:000000001d74ac0a00007ff2930a326f
How can I set the value of menu.label to get something more appropriate? I have tried, in my Admin subclass, to override the following:
protected function configureTabMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null) {
$this->configureSideMenu($menu, $action, $childAdmin);
}
protected function configureSideMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null) {
$menu->setLabel("Some nice label");
$menu->setName("Some nice name");
}
However, this does not change anything, even though I have verified that the methods above are called during runtime.
Finally found a good (and somewhat obvious) solution to this.
The Sonata Admin class uses an internal toString($object) method in order to get a label string for the entity it is handling. Thus, the key is to implement the __toString() method of the entity in question:
public function __toString() {
return "test";
}
The best way is to configure the $classnameLabel variable in the Admin Class :
class fooAdmin extends Admin
{
protected $classnameLabel = 'Custom Label';
}
But it have the same issue (weird name with entity path) doing it, even if it is working fine on all the others pages.
Apparently, the Sonata way of solving this is show here:
Quote:
While it’s very friendly of the SonataAdminBundle to notify the admin of a successful creation, the classname and some sort of hash aren’t really nice to read. This is the default string representation of an object in the SonataAdminBundle. You can change it by defining a toString() (note: no underscore prefix) method in the Admin class. This receives the object to transform to a string as the first parameter:
Source: https://sonata-project.org/bundles/admin/master/doc/getting_started/the_form_view.html#creating-a-blog-post

Symfony2, FOSUserBundle, authentication with cookies disabled

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.

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