Joseph Silber https://josephsilber.com/index.php A blog by Joseph Silber Mon, 05 Jun 2017 13:23:57 +0000 en-US hourly 1 Getting the current user (or other session data) in a Laravel controller constructor https://josephsilber.com/index.php/posts/2017/01/23/getting-current-user-in-laravel-controller-constructor Mon, 23 Jan 2017 00:00:00 +0000 https://josephsilber.com/index.php/posts/2017/01/23/getting-current-user-in-laravel-controller-constructor Session data is not available directly in controller constructors. Let's see how we can work around that.

]]>
Getting the current user (or other session data) in a Laravel controller constructor

Let's discuss something many people have accidentally discovered: you cannot use session data directly in a controller's constructor.

This change was introduced in Laravel 5.3, when the middleware pipeline was rewired to make global scopes work with session data.

In this post we'll examine the problem in more detail, and look at various ways to solve it.

The Problem

There are many reasons people want session data in the controller's constructor. Let's look at 2 examples:

  1. Sharing the current user to all views:

    Instead of using auth()->user() in your views, it's sometimes nicer to just have a global $user variable available across all views. A quick way to achieve that is to add a constructor to the base controller and share it from there:

    public function __construct()
    {
        view()->share('user', auth()->user());
    }
  2. Setting a property on the controller that needs the session:

    If you need the same piece of information in all of your methods, you may opt to set it as a property on the controller:

    public function __construct()
    {
        $this->account = Account::find(session('account_id'));
    }

In both of these cases, the code in the constructor assumes that the session is primed and ready. But if we try to run the code, it'll fail. At this point, the request has not yet been routed and no session data is available.

Solution #1: The Authenticated event

For every request, Laravel will fire the Authenticated event when the user is authenticated. You can listen for this event in a service provider, and share the $user from there:

use Illuminate\Auth\Events\Authenticated;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $this->app['events']->listen(Authenticated::class, function ($e) {
            view()->share('user', $e->user);
        });
    }
}

Solution #2: Inline view composer

If you'd rather share the user from the base controller, you can use an inline view composer:

public function __construct()
{
    view()->composer('*', function ($view) {
        $view->with('user', auth()->user());
    });
}

Since the callback will only execute when the view is composed, you'll have access to the session.

Solution #3: Converting a property to a method

Let's go back to the second example in the problem section. Instead of finding the current account directly in the constructor, you can put its logic into its own method and only call it when you actually need it:

public function index()
{
    $account = $this->account();

    // use it as before...
}

protected function account()
{
    return Account::find(session('account_id'));
}

This is great for new projects, but can be a little tedious if you already have a big project that relies on the property being available across all controller methods. For such cases, you can use an inline middleware.

Solution #4: Inline middleware

Inline middleware are a little more involved than the other solutions, but are probably the most flexible.

You may have seen $this->middleware('auth') before, which registers an existing middleware directly from within the controller constructor. What you may not know is that you can actually use a closure to define the middleware on the fly.

Using an inline middleware, we can hook into the request's routing, so that we have full access to the session:

public function __construct()
{
    $this->middleware(function ($request, $next) {
        $this->account = Account::find(session('account_id'));

        return $next($request);
    });
}

We can also use an inline middleware to share the $user to all views:

public function __construct()
{
    $this->middleware(function ($request, $next) {
        view()->share('user', $request->user());

        return $next($request);
    });
}

As you can see, this is a little more complex than the other solutions, but it gives us the most flexibilty.


These 4 solutions should cover all your session needs. If there's any controller session problem that's not solved by these, or if you have any better solutions, do let me know!

]]>
Gate and authorization improvements in Laravel 5.3 https://josephsilber.com/index.php/posts/2016/08/03/authorization-improvements-in-laravel-5-3 Wed, 03 Aug 2016 00:00:00 +0000 https://josephsilber.com/index.php/posts/2016/08/03/authorization-improvements-in-laravel-5-3 With Laravel 5.2 we got a nice authorization system right out of the box. In 5.3 this got a lot of improvements and refinements. Let's take a look.

]]>
Gate and authorization improvements in Laravel 5.3

After looking at the improvements coming to authentication, let's now take a look at what's in store for authorization in Laravel 5.3.

Note: some of the features discussed below have originaly been introduced in the middle of the 5.2 cycle. However, all of these features have been tweaked and refined for 5.3, so we'll cover them here.

Laravel authorization primer

If you're already familiar with authorization in Laravel, you can skip right ahead.

Laravel's authorization is built around 2 main concepts:

  1. A Gate class. The gate is the official authority on who has what ability (i.e. who can do what). You use the gate to register a user's abilities, and later check the gate if the user can perform a given action.

  2. Policy classes. Policies are responsible for checking abilities on a single model type. For each model class in your system you want to authorize against, you'll have a matching policy class.

To see how all this works together, let's generate a policy class for a theoretical Task model, by running php artisan make:policy TaskPolicy. Once created, we'll add a simple update method that checks if the user can update the given task:

namespace App\Policies;

use App\Task;
use App\User;

class TaskPolicy
{
    public function update(User $user, Task $task)
    {
        return $user->id === $task->user_id;
    }
}

Next we should register this policy with the gate, by adding it to the AuthServiceProvider's policy mappings:

protected $policies = [
    \App\Task::class => \App\Policies\TaskPolicy::class,
];

Behind the scenes, this will register the policy with the Gate. From now on, any authorization check against a Task will be routed to the TaskPolicy.

To demonstare, let's create a simple route to update a given task:

Route::put('tasks/{task}', function (App\Task $task) {
   abort_unless(Gate::allows('update', $task), 403);

   $task->update(request()->input());
});

We check the gate if the user can update the given task. The gate will call the update method on our policy, passing it both the currently authenticated user as well as the given task (if the user is not logged in, the gate will automatically deny all ability inquiries). If the ability is not granted, we abort with a 403.

(By the way, don't you just love that abort_unless function? I mean, just look at that! It reads like a full sentence: "abort unless the gate allows updating the task". This is Laravel at its best.)

Easily authorizing requests in controllers

Controllers in Laravel use the AuthorizesRequests trait to easily authorize any given request. You can call the authorize method from within a controller and it'll check for the ability at the gate. If the ability is denied, the request will automatically be aborted with a 403 response.

To see this in action, let's move our route to a controller:

use App\Task;

class TaskController
{
    public function update(Task $task)
    {
        $this->authorize('update', $task);

        $task->update(request()->input());
    }
}

Just like before, the gate will consult our TaskPolicy's update method and return its result.

Automatic ability names

Laravel actually goes a tiny step further, and allows you to omit the ability name when calling the authorize method. When the ability name is not provided, Laravel will automatically pick up the ability name from the controller's method.

In our sample controller, calling $this->authorize($task) would still end up consulting TaskPolicy@update for the check. Pretty nifty!

In Laravel 5.2, the automatic ability name is always the name of the controller method that calls it. Given the following controller:

use App\Task;

class TaskController
{
    public function show(Task $task)
    {
        $this->authorize($task);

        return view('tasks.show')->with('task', $task);
    }

    public function create()
    {
        $this->authorize(Task::class);

        return view('tasks.create');
    }
}

...you would have a policy with methods whose names match up with the names of the controller's methods:

use App\Task;
use App\User;

class TaskPolicy
{
    public function show(User $user, Task $task)
    {
        // check if the user can view the given $task
    }

    public function create(User $user)
    {
        // check if the user can create new tasks
    }
}

The problem with matching ability names to method names

Update August 8, 2016: after publishing this article, Taylor made some additional changes to authorization. The following has been updated to reflect these changes.

On the surface this seems quite intuitive, but if you looks closely you'll find some cracks in this convention.

Consider these 2 samples of where this convention breaks down:

  1. Checking abilities outside of a controller. The controller's method name is not always the most intuitive way to check an ability. Let's examine this template, where we loop through and check each task:

    @foreach ($tasks as $task)
        @if ($user->can('show', $task))
            <a href="{{ url('tasks', $task) }}">View task</a>
        @endif
    @endforeach

    Hmmm... That doesn't look quite right. What does $user->can('show', $task) check for? Does it check if the user can show tasks? What does that even mean? We want to know if they can view it, not show it!?

  2. Duplicate policy methods. The check for whether we should show the form to create a new task is the very same check that determines whether we allow the user to subsequently store that task. Naively mapping the controller's method names leads to the policy having two identical methods:

    use App\User;
    
    class TaskPolicy
    {
        public function create(User $user)
        {
            // check if the user can create new tasks
        }
    
        public function store(User $user)
        {
            // check if the user can create new tasks
        }
    }

    These two run the very same check, but we need them both for the two different methods on the controller. The same duplication applies to edit and update.

Normalizing ability names

In Laravel 5.3, the automatic ability names are getting an upgrade. Instead of blindly using the method name, Laravel will pick a sensible ability name based on the action you're trying to complete. If you're trying to view an item, it'll use the view ability name. If you're trying to create a new record, it'll use the create ability name, even if you call it from the controller's store method.

Laravel does this via a simple mapping between the methods and abilities. Here's the complete map straight from the source:

[
    'show' => 'view',
    'create' => 'create',
    'store' => 'create',
    'edit' => 'update',
    'update' => 'update',
    'destroy' => 'delete',
]

This happens automatically, with no change to the code in your controller. You can now check if $user->can('view', $task) in your views, which is much more intuitive.

Now that the controller doesn't always call the gate with ability names matching the controller methods, the policies should no longer have those methods. Given this controller:

use App\Task;

class TaskController
{
    public function create()
    {
        $this->authorize(Task::class);

        return view('tasks.create');
    }

    public function store()
    {
        $this->authorize(Task::class);

        Task::create(request()->input());
    }
}

...you would have a policy with just a create method:

use App\User;

class TaskPolicy
{
    public function create(User $user)
    {
        // check if the user can create tasks
    }
}

This create method wil be called from both create and store in the controller. Much cleaner!

Full policy generator

To help out with these changes, the policy generator is getting a nice boost. If you pass a model name to the generator it'll fully stub out the necessary policy methods to cover all default resource controller methods:

php artisan make:policy TaskPolicy --model=Task

You can see the raw stub in Laravel's source code, which should give you a better picture about the methods being generated.

The can middleware

While authorizing abilities inline works well, sometimes it makes more sense to do it at the route level - such as when the same ability applies to a whole group of routes. Laravel ships with a can middleware just for this purpose.

Let's define a general ability named access-dashboard:

Gate::define('access-dashboard', function ($user) {
    return $user->isAdmin();
});

To guard a whole group of routes against unauthorized access, we can wrap them in a group with the can middleware:

Route::group(['middleware' => 'can:access-dashboard'], function () {
    // define all dashboard routes...
});

Now the gate will be checked for the access-dashboard ability before running any routes in this group.

You can also pass a second argument to the middleware. This can be either the model's class name, or - to check for a given model instance - the model's route parameter name:

Route::get('tasks/{task}', [
   'uses' => 'TaskController@show',
   'middleware' => 'can:view,task',
]);

Route::post('tasks', [
   'uses' => 'TaskController@store',
   'middleware' => 'can:create,App\Task',
]);

When passing a route parameter name, the middleware will pull the model instance from the route itself. Refer to Laravel's documentation for more on route model binding.

Authorizing a full resource controller

Another neat gem in the authorization system is the authorizeResource method now available in controllers. This has the potential to considerably clean up your controllers. So what does this magical authorizeResource do?

In short, it replaces all your calls to authorize in the various controller methods with a single call in the constructor:

use App\Task;

class TaskController
{
    public function __construct()
    {
        $this->authorizeResource(Task::class);
    }

    // ... all resource methods ...
}

That's all it takes! You can now skip all calls to authorize in the individual methods. Laravel will now automagically check the right abilities for any given action. It's pretty magical actually.

And... that's it.

There are many more subtle improvements to authorization than what we've covered, but we have to stop somewhere. If you're brave enough, take a peek at the source code and you're sure to find some gems of your own.

]]>
The new Closure::fromCallable() in PHP 7.1 https://josephsilber.com/index.php/posts/2016/07/13/closure-from-callable-in-php-7-1 Wed, 13 Jul 2016 00:00:00 +0000 https://josephsilber.com/index.php/posts/2016/07/13/closure-from-callable-in-php-7-1 Let's take a look at a neat little feature coming in PHP 7.1: easily converting any callable into a proper Closure using the new Closure::fromCallable() method.

]]>
The new Closure::fromCallable() in PHP 7.1

With PHP 5.5 going EOL earlier this week and the PHP 7.1 beta expected later this month, now sounds like a good time to look into a neat little feature coming in 7.1: easily converting any callable into a proper Closure using the new Closure::fromCallable() method.

A short refresher on closures in PHP

If you're already familiar with closures in PHP, you can skip right ahead.

PHP has had closures since version 5.3. Think of it as similar to an anonymous function in Javascript; they're usually passed off to another method, which either calls it directly or stores it for later use.

As an example, let's look at Laravel's collection class. It has a reject method, which you can use to filter out values you don't want in the collection:

$ages = collect([3, 7.5, 12, 19.5, 23]);

$adults = $ages->reject(function ($age) {
    return $age < 18;
});

Behind the scenes, the reject method will call our closure callback for every item in the collection. If the age is less than 18, it will be rejected from our collection.

When you need to convert a callable to a Closure

Now imagine you want to reject every value in the collection that is not a whole number. Basically, reject all floats. There's a built-in is_float function, so why don't we pass that in directly?

$ages = collect([3, 7.5, 12, 19.5, 23]);

$integers = $ages->reject('is_float');

Unexpectedly, this will not work. Why? In addition to a closure, Laravel also allows you to pass in a single value to reject, which will remove that value from the collection. Since we've passed it a string, Laravel thinks we want to remove the 'is_float' item from the collection. Whoops!

The way around this is to actually pass in a closure and call is_float from within our closure callback:

$ages = collect([3, 7.5, 12, 19.5, 23]);

$integers = $ages->reject(function ($age) {
    return is_float($age);
});

Introducing Closure::fromCallable()

In PHP 7.1, the Closure class has a new static fromCallable method, which automatically creates a Closure from any callbable you pass it. Using this new method, we can clean up our code a bit:

$ages = collect([3, 7.5, 12, 19.5, 23]);

$integers = $ages->reject(Closure::fromCallable('is_float'));

Pretty neat!

Creating a closure that wraps a private method

Another scenario where you'd want a closure is if you want to pass in an array-callable of a private method. As mentioned in the PHP docs, you can create a callable to a method on an object by using an array with this format: [$object, 'method']. This can be quite convenient when using a method on your class that has the logic for the callback:

$due = $orders->filter([$this, 'isDue']);

This works well, with the only caveat being that the isDue method now has to be made public - otherwise the collection class won't be able to access it.

This is where Closure::fromCallable() once again comes to the rescue. When used from within a class, the resulting closure will automatically be bound to that class, allowing it to access private methods:

$due = $orders->filter(Closure::fromCallable([$this, 'isDue']));

The isDue method can now stay private or protected, but the collection will still be able to access it through the wrapping closure!

None of this is really earth shattering, but it's a neat little addition that can come in handy every so often.

]]>
Improvements to authentication in Laravel 5.3 https://josephsilber.com/index.php/posts/2016/07/10/authentication-improvements-in-laravel-5-3 Sun, 10 Jul 2016 00:00:00 +0000 https://josephsilber.com/index.php/posts/2016/07/10/authentication-improvements-in-laravel-5-3 Authentication has gotten some nice improvements in 5.3, so let's examine it piece by piece. We'll start with the current state of affairs, then take it from there.

]]>
Improvements to authentication in Laravel 5.3

Authentication has gotten some nice improvements in 5.3, so let's examine it piece by piece. We'll start with the current state of affairs, then take it from there.

Authentication in Laravel 5.2

In Laravel 5.2, you generally use one of the following methods to make sure a user is authenticated:

  1. The auth middleware. Using this middleware on a route, you can restrict access to logged-in users. It will redirect unauthenticated requests (or, for an AJAX request, respond with a 401).

  2. The guard's check method. Using auth()->check() in your code, you can check if the user is logged in, and branch your logic from there.

This is all nice and dandy, but it can potentially lead to a lot of duplicated code.

If you need some custom logic for an unauthenticated user, using auth()->check() works perfectly. However, most of the time you simply want to redirect an unauthenticated user (or return with a 401 if it's an AJAX request). The logic for this currently lives in the auth middleware, so there's no way to reuse it. Utilizing auth()->check() in your code means you have to replicate this logic all over again:

public function view(Request $request)
{
    if (! auth()->check()) {
        if (($request->ajax() && ! $request->pjax()) || $request->wantsJson()) {
            return response()->json(['error' => 'Unauthenticated.'], 401);
        } else {
            return redirect()->guest('login');
        }
    }

    // The user is logged in. Proceed.
}

Clearly this is a lot of extra code that we shouldn't need here. Ideally, there should be a single global location for handling unauthenticated responses. Lucky for us, Laravel 5.3 makes this a breeze!

Introducing the authenticate method

In Laravel 5.3, there's now a better way: the authenticate method. Calling authenticate will throw an exception if the user is not logged in, so that you don't even need a conditional in your code:

public function view()
{
    $user = auth()->authenticate();

    // The user is logged in. Proceed.
}

If the user isn't logged in, it'll throw an AuthenticationException. There's no need for you to catch it - Laravel gracefully handles this for you in a single global location.

The exception handler's unauthenticated method

The global exception handler catches all authentication exceptions and calls its unauthenticated method. It'll redirect the request to the login page, or, for an AJAX request, return with a 401 error status code.

You can customize this method to your liking, giving you full flexibility on how to handle unauthenticated requests in a single centralized location.

The Authenticate middleware

In Laravel 5.2, the Authenticate middleware ships in the app skeleton. This middleware is responsible for creating a valid response for an unauthenticated request. To customize its functionality, you edit the handle method directly in the middleware class.

In Laravel 5.3, the auth middleware has been pulled into the core, allowing us to properly cover it in our tests. Since the middleware does not ship with the app skeleton anymore, you cannot customize it directly. Instead, the auth middleware now throws an AuthenticationException when the user is not logged in, allowing the global exception handler to catch it. As mentioned before, you can customize the response there.

Authenticating against multiple guards

Another neat addition to the auth middleware is authentication against multiple guards.

Imagine you're using both the web & api guards throughout your app. You may have some routes that you want to be accessible if the user is logged in with either one of those guards. The updated middleware makes this a breeze:

Route::get('users', [
    'uses' => 'UsersController@index',
    'middleware' => 'auth:web,api',
]);

The auth middleware will check each guard. If the user is logged in to any of the guards, the request will be authenticated. Additionally, that guard will be set as the default, so that any calls to auth()->user() throughout your app will properly return the user.

Route model binding and global scopes

The final enhancement to authentication we'll cover here is with regards to route model binding. Route model binding is one of my favorite features in Laravel, removing boilerplate all over your controllers. For the uninitiated, here's a short primer from the docs:

Route model binding provides a convenient way to inject model instances into your routes. For example, instead of injecting a user's ID, you can inject the entire User model instance that matches the given ID.

As people started using route model binding more and more (especially since 5.2 introduced implicit model binding), they started running into the following issue: if you have a global scope on your model that tries to access the current user, well... you can't. That's because route model binding runs before authentication, so when the model resolves the current user has not yet been authenticated.

Here's some code to explain this issue with a little more detail:

// routes.php
Route::get('invoices/{invoice}', [
    'uses' => 'InvoicesController@show',
    'middleware' => 'auth',
]);

// InvoicesController.php
public function show(Invoice $invoice)
{
    // show the invoice
}

// Invoice.php
class Invoice extends Model
{
    protected static function boot()
    {
        static::addGlobalScope(function ($query) {
            $query->where('user_id', auth()->id());
        });
    }
}

We're restricting all queries on the invoice model to just the ones that belong to the current user (this is obviously a bit contrived to keep things simple. For a real-world example, check out Teamwork's excellent UsedByTeams trait).

The problem, as mentioned above, is that auth()->id() will return null when the model is being resolved through the route's model binding, since it runs before the auth middleware.

In Laravel 5.2 there was really no way around this issue. The only remedy was to forgo route model binding and fetch the model yourself:

// InvoicesController.php
public function show($id)
{
    $invoice = Invoice::findOrFail($id);

    // show the invoice
}

Not the end of the world, I know, but once you've been spoiled by route model binding there's really no going back. Laravel's neat little features tend to have that effect on you...

If you've ever run into this issue before, you'll be happy to hear that in Laravel 5.3 this problem is no more. The middleware stack has been rewired so that authentication will now always run before route model binding and you can now safely get the authenticated user in your global scopes. Hallelujah!

Note: as part of this change, session data is no longer available directly in a controller constructor. If you ever run into that, here are some workarounds.

Bonus: the request's expectsJson method

While not directly tied to authentication, I thought it'd be nice to include this little tidbit in here.

As you can see in the first code snippet in this post, determining whether the request needs JSON can be quite tedious. If it's an AJAX request it probably wants JSON, unless it's actually a PJAX request. If it's a regular HTTP request, there may be an Accept header specifying that the response should be JSON.

To make all this easier, there's a new expectsJson method on the request. You can use that directly to determine whether to repond with JSON or not:

public function view(Invoice $invoice)
{
    if (request()->expectsJson()) {
        return $invoice;
    } else {
        return view('invoices.show', compact('invoice'));
    }
}

Just a little tip if you ever have a need for this.

]]>