Symfony Access Decision Manager




Yeah, I can’t spell.

I just hit upon a problem whereby I needed my Symfony security voter setup to always deny access even when one of my voters returned an Affirmative.

In this case I want to always deny.

By default Symfony will allow access if at least one voter returns GRANTED.

If you want to know more, check out this video I created about this very problem, and some potential solutions.

Gotcha: Upgrading To FOSRestBundle 2.0

This is not a huge issue, and likely quite easy to identify and fix. But I’m the kind of person who hits Google before engaging brain, and I didn’t find a direct answer (boo, wake up brain!).

I have, this very evening, decided to try FOSRestBundle 2.0. It’s currently still in development, so isn’t available without a little naughty composer tagging:

A little tip there – if you use the  @dev after your requested version number, you can force through development packages without globally changing your project’s  minimum-stability  setting.

Anyway, being that I’ve been coding for the last 12.5 hours, I went with the lazy man’s option of copy / pasting my config from a FOSRestBundle 1.7 installation I happened to have laying around.

Firstly, I got this:

Which confuses me, as this seems to be the same as what’s in the configuration reference.

Anyway, simply removing that line makes the error go away.

The next error I got was this:

And what this turns out to be is that the  mime_types must now all be arrays:

So there you go 🙂

Checking An Edge Case With Symfony Voters

I love Symfony Voters. The Access Control List is, in the vast majority of cases, totally overkill for my security needs. Symfony’s Security Voters are a very quick and easy alternative.

Yesterday I got caught out by an edge case I had not been testing for.

A little background on this – I have been creating a RESTful API using Symfony 3, Behat 3, PHPSpec, and a few other interesting technologies. If you are interested in knowing a little more about this then be sure to check out the video tutorial series here on CodeReviewVideos where I have been documenting as I go.

The video tutorial series covers three different entities – Users (FOSUserBundle), Accounts, and Files (with FlySystem).

I want to ensure the currently logged in User has access to the resources they are requesting. I’ve been doing this with Voters.

It’s likely easier to follow along with these words if I give them more context. Here’s the code for the File Voter:

And the implementation:

This all works and is passing.

However, there is an edge case I hadn’t considered.

What happens if $requestedFile->belongsToUser($loggedInUser) fails, or returns null? Well, bad times as it happens.

Why might it fail? Well, let’s look at that interface, and the real method implementation:

And the implementation:

Due to a mistake in my fixtures, the File wasn’t correctly being added to the Account I expected, so the call of getAccounts() was returning null.

In this case, I first fixed my fixtures as that situation should never really happen.

But what if it does? I don’t want to be throwing errors to the user. It’s easier to deny them access, log the fault (using monolog, and/or to ELK), and fix it in a slightly more controlled manner.

I added the following two specs to my FileVoterSpec test file:

And then changed up the implementation (uglify!):

Note the try/catch block. It’s definitely made the code uglier, but it ‘fixes’ a potential problem I hadn’t considered.

I say ‘fixes’ as this should never happen. But if I have learned anything over the last 15-odd years, it’s that those things that should never happen… they happen.

Custom Route Name in FOSRESTBundle

I recently had an issue where I wanted to have a controller called SocialMediaProfilesController, but whatever I tried, the automatically generated route names kept coming out as e.g. get_socialmedia_profiles.

I haven’t dived deep into the code to determine as to why this weird spacing issue is occurring, but my guess would be that if you created a controller called SomeEvenLongerStringController, your action might be get_someevenlonger_string. But as I say, I haven’t tried this, so it’s just a guess as to what might be happening.

Anyway, the fix is pretty simple. It’s probably out there on Google, or in the docs, or something, but I couldn’t find it. Instead, I stumbled upon this and it works, so here we go:

By using the RouteResource annotation, you can control how the route names will be generated.

Without the annotation:

And then with:

So that’s pretty handy.

Debugging By Dumping

By now, most of us will likely have used the  dump($var); function made available for us in Symfony 2.6.

If you haven’t, or you think you can’t because you a) don’t have the latest and greatest version of Symfony, or b) don’t use Symfony, then never fear, you can include the VarDumper component in any project. No excuses.

Whilst the   dump($var); function is incredibly useful, in one instance so far, it has caused me more headaches than it has solved.

The Situation

I’m working on a new RESTful API using Symfony 2 as my back end.

Along the way, I want to restrict Users from being able to access other peoples data, for obvious reasons.

Without getting too deep into the code – as it’s really not that important to this problem / solution – I hit upon an issue where I wasn’t getting the expected output. Such is life of a developer.

However, I can tell you are getting twitchy from having not yet seen any code, so to placate your code cravings, here is the method:

See, I told you, not much to look at.

At some point along the way (and in truth, I haven’t figured out exactly where as of yet), I know that  $this->denyAccessUnlessGranted('view', $account);  is having a bad time.

To begin with, I wanted to see exactly what the contents of  $accounts was before it even hit the  foreach loop.

To the dumper!

Alas, no:


The next thing you might think is – ok, just use  print_r($var); and be done with it.

Unfortunately, that’s not an option when dealing with most Doctrine entities. If you try, you may get lucky, you may hit funky maximum nesting level errors, or worse, you may crash your DHC client. Guess which I encountered?

My Solution

Never the less, there is a solution. Quite an old school solution. Something I used to use all the time before the VarDumper component was a thing:

This will give you an equivalent of  print_r($var); but without the nasty blow ups:


It’s an old technique sir, but it checks out.

Now just to figure out why the test is failing, and everything will be right with the world once more.