With Symfony2 the firewall comes with a built in feature: impersonate a user. We’ve been using impersonation as an admin tool for about 5 years as it is very effective for troubleshooting. When a user files a support ticket saying something isn’t showing properly to them or they are getting random errors it is very easy to just quickly switch to that user and see what they are seeing. As with all features, this one may not be appropriate for your application if your user expects no administrative staff to have access to his or her account.
While Symfony’s built in impersonation feature is a great step up from having to build it by hand, it still can be a bit more friendly. We’ve seen two additional functions we wanted the impersonation to handle. First, we wanted it to on exit from impersonating the user returns the user to where the user first started to impersonating. Currently it just brings you back to wherever you link the user. Second, if already impersonating a user and trying to start to impersonate another, we didn’t want it to throw an error but to quietly switch you. This functionality could lead to unwanted circumstances if an impersonating user believes they can impersonate another user, and then slowly just keep exiting impersonation of each user and go back up the chain they went down. However, in our situation the time admins hit this was when they’d impersonate one user, realize they clicked the wrong one, click back and try to impersonate a different user. As the browser uses it’s cached page when the user hits back they see the list of users as if they were an admin and can click on the correct user. If they do this they are hit with a 500 error, “You are already switched to X user”.
For both of our goals we overrode the built in switch user class. It is really easy to override, as all you need to do is specify in your parameters.yml “security.authentication.switchuser_listener.class: My\AppBundle\Listener\SwitchUser”. We used the built in class as our starting template: https://github.com/symfony/symfony/blob/2.5/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php Our final class ended looking like:
Here are the specifics on what everything we did and why.
First feature: Redirecting the user on exiting impersonating a user to where they originally started impersonating them. As we didn’t want to go around our entire application updating logic for the exit impersonation links if we decided to later change the behavior, we decided to build the redirect into the class itself. We didn’t want to rely on the user’s browser referrer header, so instead we decided to on the links to impersonate a user to include a “returnTo” parameter. This parameter is set to the current URI (app.request.uri). At line 97 we save the returnTo parameter to the session, for later use. On line 93, as a user is switching (in this case exiting) a user, if the session has a stored “returnTo” URL, we assign it to the “$overrideURI” variable. On line 107 we have a bit of logic on if we redirect them to the default route or the “returnTo” URL. The reason for the additional “$this->useOverrideURI” variable on this line is for our second feature of switching between users when you are already impersonating one. As the logic all runs through the same routine, if you are simply switching to a new user from an already impersonated one, we don’t want to redirect you back to your original URL when you started all the impersonating, so we disregard the redirect in this case and redirect to the default route. An example of this is admin impersonates user A, then wants to impersonate user B. Upon impersonating user B, the admin does not want to be redirected back to the admin dashboard (the sessions returnTo URL), but to where the impersonate user link is pointing to (User B homepage).
Second feature: Allow users to impersonate a different user while already impersonating another. One Line 134 is where the original SwitchUserListener would usually throw a 500 error as you are already impersonating a user. Instead, we make sure that the original token has the appropriate permissions, if so it will not throw an exception. Line 159 is the other main update for this feature. If you are already impersonating a user and try to impersonate another user, upon exiting you want to go back to your original user. Now if a original impersonation token (user) exists, we keep that as the user you’ll be switched to when you exit the impersonation.
Recently when I was working on a client project we had a bunch of permissions which had a hierarchy (or tree structure). For example, you needed Permission 1 to have Permission 1a and Permission 1b. In the examples below lets assume `$choices` is equal to the following:
At first, I used the built in in optgroups of a the select box to output the form, so it was clear what permissions fell where. My form would look similar to:
Multiple select boxes aren’t the easiest to work with as we all know. Also, it isn’t as easy to visually see the difference as the height of the select box could not be long enough to show you what an optgroup’s title is. Instead, I decided to use the checkbox approach. Issue with this, the current Symfony2 form themes don’t output checkboxes in groups or with any visual indication of the hierarchy. I ended up creating my own custom field type so I could customize the way it renders globally via the form themeing. My custom type just always set the choice options to expanded and multiple as true. For the actual rendering, below is what I ended up with.
Since a picture is worth a thousand words, here is an example of what it looks like working:
Let me know if you have any questions! Happy Friday.
Unfortunately, setting something like this up with the default “pattern” setting in your security.yml file isn’t possible. The “pattern” setting only matches on the route URL, not the parameters so there’s no way to have it selectively trigger when a parameter is present on a URL. So how do you do it? Well as it turns out, there’s a firewall configuration called “reuqest_matcher” which lets you “match” a firewall using a service. Just create a service that extends the RequestMatcherInterface, implment a “matches” function, and then add the class as a service.
Our code for the service ended up looking like:
And then the actual firewall configuration ends up being:
You don’t need a “pattern” setting anymore since the “matches” function supersedes it. Anyway, let me know if you have any questions!
At the beginning of the summer we decided to redo our website. The design on the old site was looking a bit dated and more importantly the content didn’t really reflect the types of projects we’re looking to work on. From a technology perspective, our old site was built on WordPress with the explicit goal of being able to share the same WordPress theme as our blog. The two sites did in fact share the same theme but looking back, we never updated the main site to really make it “worth it”. With that experience in mind, we started looking around for what we could use to build setfive.com.
Stepping back and looking at our requirements, we really don’t need a CMS. I’d argue this holds true for most website projects when there’s less than 20 pages, everyone who might edit it is technical, and the content isn’t updated frequently. Specifically looking at some major WordPress features, we don’t need the WYSIWYG editor, plugin ecosystem, media handling, or theming capabilities. So what capabilities do we need?
There’s certainly more capabilities static websites could need but I think this is a decent list for the “general” case and it captures our requirements. After doing some research, it looks like there’s currently a few options that would satisfy these requirements:
I ultimately chose Silex because our team has deep PHP experience, especially with Symfony2. Because of that we’d be right at home with the Routing component and of course Twig for templating.
OK so how do you actually get this to work? I ran across Jonathan Petitcolas’s Building a static website with Silex post and used it as a guide. Here are the actual commands you’d need to get this all setup though:
Now, you just need to create a file named “index.php” which contains:
And finally, in the “views” directory add a file called “index.html.twig” which contains some content. If you have a web server setup, just point a vhost at the “web” directory, load it, and you should see the content of your index file.
If you don’t have a web server setup, a nifty trick via Gonzalo Ayuso, create a in the “web” directory named “router.php” containing:
And now, you can start the built in PHP 5.4+ server by running:
You can load your Silex app by loading http://localhost:8888 in your browser.
Anyway, as always questions and comments are welcome!
Posted In: PHP
As far as type systems go, PHPs is pretty schizophrenic. You’ve got primitive types, like strings and booleans, the ubiquitous “array” type, and then user defined classes. Most of the time, the type system is invisible since it barely enforces anything. Especially for basic types and the standard library, you can almost always use strings, booleans, and integers interchangeably without much complaining from the interpreter. Where things go sideways is when you start using user defined types, especially with type hinting.
Imagine we’ve got the following setup:
If you run that in a terminal, PHP will throw the following error:
PHP Catchable fatal error: Argument 1 passed to sayHello() must be an instance of Dog, instance of Pet given, called in /home/ashish/Downloads/dog.php on line 19 and defined in /home/ashish/Downloads/dog.php on line 14
Because even though every “Dog” is by definition a superset of the “Pet” class, PHP doesn’t see it that way. And now, our original problem. In most other object oriented languages, you’d be able to simply typecast the instance of Pet to a Dog and then call the function as expected. Unfortunately, PHP doesn’t natively support typecasting so we’re stuck looking for a crazy workaround. Enter Reflection. PHPs reflection library lets you do all sorts of nefarious things, like manipulating private properties and retrieving the source for an arbitrary object.
So how do you use it to do a bootleg typecast? It’s actually pretty straightforward:
The “copyShimmedObject” is the money maker. It basically pulls the private properties out of the “from”, makes the property public, and then sets them on the “to” object. If you run the sample you’ll get the expected output instead of the error above:
Hello: Fluffy of destroyer of worlds