Symfony2 impersonation route parameters missing - symfony

I receive an error when I change pages if I am impersonated as another user in Symfony2. It only happens when the route has additional parameters. There is no sign of route generation at the pointed line number.
Controller action
/**
* #Route("/member/{id}", name="member_page")
* #Template()
*/
public function memberAction($id)
Error
An exception has been thrown during the rendering of a template ("Some mandatory parameters are missing ("slug") to generate a URL for route "member_page".") in members.html.twig at line 2.

Do you have two routes with the same name?
Watch your routing.yml file or the class annotation, maybe you have defined a prefix with slug parameter.

If the "slug" parameters is not necessary, provide a default for that as NULL. Here is an example
message_edit:
pattern: /edit/{slug}
defaults: { _controller: CommunicationBundle:Default:edit, slug: null }

Thanks for the answers, but there was something else going on:
The template was extending another template in which the bug could be found. Therefor an incorrect linenumber was displayed in the eror message. After setting up a smaller test environment I could replicate the problem. It was an anchor to exit impersonation which I wanted to redirect to the current page, but was obviously missing the parameters of the current page:
Stop impersonation
I fixed it for now by just using the named 'home' route.

Related

symfony No route found for "GET

I changed the name of my controller and the routes in the controller and the templates that correspond to it. I even executed the bin/console debug:route to check routes terminal
but when I type http:/localhost:8000/admin/programation/circuit I get an error : No route found for "GET /admin/programation/circuit".
error
Remark: everything was working perfectly before I change the controller name and the routes.
enter image description here
Your route is not correct. To you try to call the route
/admin/programation/circuit
But your route is
/admin/programation/circuit/
So i think that is the problem in your case. So you have to add the / or you should remove them from your action.
The trick is that with the symfony routing system we could not declare a route with a path that ends with '/' only if the path of the rotue is outright the '/' (not préfixed with any thing).
In our case, the index() function is pointed to a route prefixed by '/admin/programation/circuit' and ended with '/' otherwise the following route becomes '/admin/programation/circuit/' and as you can see, it does not respect the convention.
What I tell you, is to redefine the configuration in this way, so that the path becomes empty for the index() function. To define you route in a proper way, you can prefix even the name of the route (from version 3.4)
/**
* #Route("/admin/programation/circuit", name="admin_programtion_circuit_")
*/
class BackofficeProgramationController {
/**
* #Route("", name="index")
*/
public function index() {...}
}

Symfony (a bit more dynamic ?) routing

I am new to symfony. As an exersice I`m trying to make some basic cms.And I was wondering is this aproach of routing wrong:
/**
* #Route("/back-office/", name="back-office")
*/
public function indexAction(Request $request,$page="")
{
switch($page){
case "":
return $this->render('CmsBundle:BackOffice:index.html.twig');
break;
default:
return $this->render('CmsBundle:BackOffice:site-map.html.twig');
break;
}
}
This is my yaml confing:
back_office_pages:
path: /{page}
defaults: { _controller: CmsBundle:BackOffice:index}
By using this aproach I wont have to configure each route in the yaml file. Since routes may vary. But I am not quite sure this is the symfony way of doing things so I decided to ask for advice..
What I`m trying to achive:
Lets say we have a user that have less back-end programing expirience or not at all and he stumbuled upon the CMS. The goal is to add front end pages using some user interface. Then we store the pages(slug) in the database. In the index action we retrive this data. From the database we can also assing template to a page (we need the user to have at least some html+css+twig).
So what we do is get the pages that user added :
ex : Gallery, Contacts
we check the request url
and if the page requested is in the array from the database we return the template related to the page.
NOTE:
If you disagree with this method please do not bash me but eplain why is this wrong. Because as I said I am still new with the framework.
Try setting your routing to:
back_office_pages:
resource: "#CmsBundle/Controller/"
type: annotation
to set up Routing Annotations inside your CmsBundle.
Then, your action should be working using the url "/back-office/{page}"

Symfony2 / routing / use parameters as Controller or Action name

Is it possible to route to a controller/action using given parameters ?
For example :
my_custom_route:
pattern: /{controller}/{action}
defaults: { _controller: AcmeDemoBundle:[controller]:[action] }
I would like [controller] and [action] to be replaced by given route's parameters values.
I.E : http://www.somedomain.com/Content/add should call action "addAction" of controller "ContentController" in bundle "AcmeDemoBundle"
Yes you can do so however it is not recommended. What would you do in future of you had to refactor your code and your controller / action had to change? You may break links and functionality along with possibly losing search engine optimization that may have been done for that particular route.

add custom logic to internal Symfony classes like SwitchUserListener or TemplateGuesser

I got a problem to add custom logic to some Symfony classes.
SwitchUserListener
I want to add a check, that a user cannot switch to a another user, which have more rights/roles, than the initial user.
First attempt
Overwrite the parameter in the security_listeners.xml with the key:
security.authentication.switchuser_listener.class But where can I overwrite it?
In the security.yml it didn't work:
security:
...
authentication:
switchuser_listener:
class: Symfony\Component\Security\Http\Firewall\SwitchUserListener
Second attempt
Overwrite the service for the SwitchUserListner service id: security.authentication.switchuser_listener
I create the same service in my service.xml of my bundle, but my class was not used / called.
Another idea was to overwrite only the class, but that only works for bundles, but the SwitchUserListener was not in the SecurityBundle, it was in the symfony component directory and that seemed to me as a really bad idea to overwrite the SecurityBundle
Third attempt
Now I get the solution: First time I didn't realize that the dispatcher call listener for the SWTICH_USER event in the SwitchUserListener:
$switchEvent = new SwitchUserEvent($request, $token->getUser());
$this->dispatcher->dispatch(SecurityEvents::SWITCH_USER, $switchEvent);
So I need only to create a service with the special tag for this event type:
<tag name="kernel.event_listener" event="security.switch_user" method="onSecuritySwitchUser" />
And do the check in the given method.
This seems to be a better solution thatn the other two. But there is still a problem. In my listener for the SwitchUserEvent I need to ignore my custom check if the user wants to exit the switched user.
So I need to check the requested path: ignore if path containts '?switch_user=_exit'
But the path (URL parameter) can be changed:
# app/config/security.yml
security:
firewalls:
main:
# ...
switch_user: { role: ROLE_ADMIN, parameter: _want_to_be_this_user }
But in my bundle I can't read this parameter, because it will not be passed to the service container. It will be passed to the constructor of the SwitchUserListner class and will be saved there as private attribute, never accessable (without Reflection) from outside. (that happens here: SecurityExtension.php line 591) So what to do? Define the parameter twice go against DRY. Use Reflection?
And the other point is that there aren' every time events that will be fired on which I write a subscriber class. So what would be another / best solution for it?
I ask this question because I will get some similar problem where I want to add or overwrite something of the symfony intern components.
TemplateGuesser
I wanted to modify the TemplateGuesser: For a specific bundle all Templates which has the annotation #Tempalte the tempate file should be located with the controller TestController#showAction at this path:
Resources/views/customDir/Test/show.html.twig
So the guesser should be put and locate everything into a additional folder customDir instead of using only views. When using the render function with a specific template, the guesser should ignore the annotation.
I created my own Guesser and overwrite the service id: sensio_framework_extra.view.guesser and in comparision to the SwitchUserListener this time my class is really called instead of the original guesser. Why it works here but not with the SwitchUserListener?
Is this a good solution at all? I also tried to add a second listener, which calls the TemplateGuesser, its the service sensio_framework_extra.view.listener with the class Sensio\Bundle\FrameworkExtraBundle\EventListener\TemplateListener But that didn't work.
Whenever you need to add custom logic or extend the framework behaviour, you can use and abuse the container configuration. That means you can overwrite pretty much every service Symfony defines by just creating a new class that extends that service – or not, really – and creating the service definition for it with the same key as the original service you wanted to extend or change behaviour.
For instance, Symfony has a base template guesser registered as a service with the sensio_framework_extra.view.guesser id. If you want to extend that or change behaviour, you only need to create your own class and register it with the same id of the original service – remember that the bundles loading order affects the service definitons with the same id, where the last one loaded is the one that will be created.
That should solve both of your problems.

Symfony2 output any HTML controller as JSON

I have a website completed that was created in Symfony2 and I now want a lot of the features of the site to now be made available in a mobile app.
My idea is by appending a simple URL variable then it will output all the variables of the relevant page request in JSON.
So if I connect to
www.domain.com/profile/john-smith
It returns the HTML page as now.
But if I go to
www.domain.com/profile/john-smith?app
Then it returns a JSON object of name, age and other profile info.
My app code then receives the JSON and processes.
I can't see any security issues as it's just really the variables presented in JSON and no HTML.
By doing the above I can create all the app code and simply make calls to the same URL as a web page, which would return the variables in JSON and save the need for any more server-side work.
The question is: How would I do this without modifying every controller?
I can't imagine an event listener would do it? Maybe I could intercept the Response object and strip out all the HTML?
Any ideas as to the best-practice way to do this? It should be pretty easy to code, but I'm trying to get my head around the design of it.
There is a correct way to configure the routes for this task
article_show:
path: /articles/{culture}/{year}/{title}.{_format}
defaults: { _controller: AcmeDemoBundle:Article:show, _format: html }
requirements:
culture: en|fr
_format: html|rss
year: \d+
However, this would still require you to edit every Controller with additional control structures to handle that output.
To solve that problem, you can do two things.
Create json templates for each template you have, then replace html in template.html.twig with template.'.$format.'.twig. (Be careful to ensure users can't pass a parameter without validation in the url, this would be a major security risk).
Create your own abstract controller class and override the render method to check the requested format and provide output based on that.
class MyAbstractController extends Symfony\Bundle\FrameworkBundle\Controller\Controller
{
public function render($view, array $parameters = array(), Response $response = null)
{
if($this->getRequest()->getRequestFormat() == 'json')
{
return new Response(json_encode($parameters));
}
else
{
parent::render($view, $parameters, $response);
}
}
}
NOTE The above code is a prototype, don't expect it to work out of the box.
I personally would deem the second method more correct, because there is no duplication of code, and less security concern.

Resources