Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Close the Gate! Exploring Laravel's Authorizati...

Close the Gate! Exploring Laravel's Authorization Layer

Authorization (controlling access via permissions) is often confused with Authentication (identifying a user/request). Yet it's not as complicated as it may seem! In this talk we will dig into the many features provided by Laravel's Authorization layer (gates and policies) to control access. Along the way we will identify some tips to make things easier, look at performance and code maintenance, as well as things to consider for more advanced or complex apps. We will also cover several popular packages which offer advanced functionality, and why/when to use them.

Chris Brown

August 29, 2019
Tweet

More Decks by Chris Brown

Other Decks in Programming

Transcript

  1. Close the Gate! – Using Laravel's Authorization Layer @DrByteZC @drbyte

    drbyte.dev Chris Brown 1 @drbytezc drbyte.dev Who Am I? • Freelance developer 15+ yrs drbyte.dev • Cofounder Zen Cart e-commerce platform • Package Maintainer for 
 Spatie Laravel-Permissions package • Active contributor to Laravel Valet package • Public Speaking Coach: PickyPresenter.com 2 @drbytezc drbyte.dev The Problem: What I Needed • Contributors can edit/delete their own content. • Super-Admins can do (almost) anything. • Nobody can upvote/downvote an article more than once, even administrators and super-admins. • Users shouldn't be able to message "ignored" users, nor message others who have blocked them. 3 @drbytezc drbyte.dev Objectives 1. Explore the ways Laravel provides Authorization capabilities 2. Uncover some tips / tricks / traps 3. Discuss related Packages, and when to use them 4 Laracon EU 2019 - Close the Gate! Laravel Authorization.key - August 29, 2019
  2. @drbytezc drbyte.dev Let's Talk About Auth 5 @drbytezc drbyte.dev Auth

    != Auth Authorization is not Authentication Authentication is: verifying identity and allowing connections to the application
 (login, password, token/session/cookie) Authorization is: verifying privileges,
 for controlling access to resources within the application.
 (roles, permissions, Access Control/ACL) 6 @drbytezc drbyte.dev Terms • Permission = permissions, abilities, privileges • Role = (often) "groups of permissions" 7 @drbytezc drbyte.dev Authorization in Laravel • Gate - define rules for "abilities"/permissions (closure) • Policies - define rules regarding model actions • Middleware - route/controller rules to allow/deny • Validation - check authorization before processing inputs (Controller calls to Gate, Policy, Form Request) • View - @can, @cannot, @canany, @guest, etc 8 Laracon EU 2019 - Close the Gate! Laravel Authorization.key - August 29, 2019
  3. Show Me The Code! 9 @drbytezc drbyte.dev Gate::define // AuthServiceProvider

    public function boot() { Gate::define('edit-settings', function ($user) { return $user->isAdmin; }); Gate::define('update-post', function ($user, $post) { return $user->id === $post->author_id; }); } 10 @drbytezc drbyte.dev Gate::allows / denies class SettingsController extends Controller { public function edit() { if (Gate::allows('edit-settings')) { return view('settings.edit'); } abort(403, 'Not allowed to edit settings'); } public function update(Request $request, $id) { if (Gate::denies('edit-settings')) { abort(403, 'Not allowed to edit settings'); } // save updates } } 11 @drbytezc drbyte.dev Gate::allows - forUser() class SettingsController extends Controller { public function edit() { // $user = User::find(1); if (Gate::forUser($user)->allows('edit-settings')) { return view('settings.edit'); } abort(403, 'Not allowed to edit settings'); } } 12 Laracon EU 2019 - Close the Gate! Laravel Authorization.key - August 29, 2019
  4. @drbytezc drbyte.dev public function update(Request $request, $id) { $post ==

    Post::find($id); // abort_unless($request->user()->can('update-post', $post), 403, 'Not allowed to update'); abort_if($request->user()->cannot('update-post', $post), 403, 'Not allowed to update'); $post->save(); } $user->can() / cannot() 13 @drbytezc drbyte.dev php artisan make:policy PostPolicy --model=Post class PostPolicy { use HandlesAuthorization; public function viewAny(User $user) { } public function view(User $user, Post $post) { } public function create(User $user) { } public function update(User $user, Post $post) { } public function delete(User $user, Post $post) { } public function restore(User $user, Post $post) { } public function forceDelete(User $user, Post $post) { } } 14 @drbytezc drbyte.dev Policy Methods (return: boolean) class PostPolicy { use HandlesAuthorization; public function view(?User $user, Post $post) { // everyone can view all visible posts return (bool)$post->isVisible; } public function update(User $user, Post $post) { // can update their own post return $user->id === $post->author_id; } } 15 @drbytezc drbyte.dev Controller: $this->authorize() class PostsController extends Controller { /** * @param $id * @throws \Illuminate\Auth\Access\AuthorizationException */ public function edit($id) { $post = Post::find($id); // PostPolicy@update $this->authorize('update', $post); return view('posts.edit'); } } 16 Laracon EU 2019 - Close the Gate! Laravel Authorization.key - August 29, 2019
  5. @drbytezc drbyte.dev $this->authorize() with extra context class PostsController extends Controller

    { public function update(Request $request, Post $post) { $this->authorize('update', [$post, $request->input('category')]); // The current user can update the blog post... } } 17 @drbytezc drbyte.dev Policy Method with extra context class PostPolicy { use HandlesAuthorization; public function update(User $user, Post $post, int $category) { // can update their own post, if it is part of category 3 return $user->id === $post->author_id && $category > 3; } } 18 @drbytezc drbyte.dev Middleware: 'can:' Route::put('/post/{post}', function (Post $post) { // The current user may update the post... })->middleware('can:update,post'); 19 @drbytezc drbyte.dev Blade: @can() <div class="card-body"> @can('update-post', $post) The edit form could go here. @endcan </div> 20 Laracon EU 2019 - Close the Gate! Laravel Authorization.key - August 29, 2019
  6. @drbytezc drbyte.dev Blade: @can family @can(ability1) @elsecan(ability2) @endcan @cannot(ability3) @elsecannot(ability4)

    @endcannot @canany([ability5, ability6]) @elsecanany([ability7, ability8]) @endcanany 21 @drbytezc drbyte.dev can() fallback/precedence 1. Policy Method for the Model 2. Gate ability by that name $user->can('update', $post); 22 @drbytezc drbyte.dev Super-Admins 23 @drbytezc drbyte.dev Gate::before() -- Super-Admins // AuthServiceProvider public function boot() { // Super Powers // always return true for any Gate 'can' check, if user isSuperAdmin Gate::before(function ($user, $ability) { return $user->isSuperAdmin ? true : null; }); } 24 Laracon EU 2019 - Close the Gate! Laravel Authorization.key - August 29, 2019
  7. @drbytezc drbyte.dev Policy::before() -- Super-Admins class PostPolicy { use HandlesAuthorization;

    // Super Powers public function before($user, $ability) { if ($user->isSuperAdmin) { return true; } } public function update(User $user, Post $post) { return $user->id === $post->author_id; // eg: update their own post } } 25 @drbytezc drbyte.dev Super-Admins • Policy::before() • Gate::before() • Gate::after() • Impersonation * 26 @drbytezc drbyte.dev Storing Dynamic Permissions in the Database class User extends Authenticatable { public function permissions() { return $this->belongsToMany(Permission::class); } public function hasPermissionTo(string $ability) { $permission = Permission::where('name', $ability); if (! $permission) { return false; } return $this->permissions->contains('id', $permission->id); } } 27 @drbytezc drbyte.dev Registering Permissions from the Database // AuthServiceProvider public function boot() { // skip trying to register permissions if not migrated if (! Schema::hasTable('permissions')) return; // Tip: would probably want to cache this $permissions = Permission::all(); $permissions->map(function ($permission) { Gate::define($permission->ability, function ($user) use ($permission) { return $user->hasPermissionTo($permission->ability); }); }); } 28 Laracon EU 2019 - Close the Gate! Laravel Authorization.key - August 29, 2019
  8. @drbytezc drbyte.dev Validation in Form Requests class PostRequest extends FormRequest

    { public function authorize() { $post = Post::find($this->route('post')); return $post && $this->user()->can('update', $post); } } 29 @drbytezc drbyte.dev Front-End Clients • Authorization and Front-End Security: • Usually okay to expose allowed permissions to the front-end. • Just don't trust them. :) • Always rely on the back-end application to enforce them, 
 particularly for sensitive actions and accessing sensitive info. 30 @drbytezc drbyte.dev Best-Practices • Test "abilities", not "roles".
 - ie: use can() everywhere • better for code maintenance, gate centralization • RESTful Controller Method names • policy events tie well with RESTful controller methods, which indirectly will make you "think" in terms of effective "ability" names in your app 31 @drbytezc drbyte.dev When To Use Packages 1. First, ensure you've mapped out your permission needs. 2. Second, build your static rules into your app before using a package. (ie: Policies to control model event rules) 3. Next, do you need Dynamic capability? 
 ie: for users to grant/revoke access to other users? 4. Consider how many layers of hierarchy you require? 
 Do you need *both* roles and permissions? Or just one?
 Then assess Packages based on your needs. 5. Less is more. Know what the package does! 
 Does it do too much? Can you understand its API? 32 Laracon EU 2019 - Close the Gate! Laravel Authorization.key - August 29, 2019
  9. @drbytezc drbyte.dev Popular ACL Packages Spatie/Laravel-Permission Santigarcor/Laratrust Zizaco/Entrust Silber/Bouncer Lab404/Laravel-Impersonate

    * 33 34 @drbytezc drbyte.dev Review • Gate • Policy • Middleware • Validation • View • Fallbacks • Front-End Security • Best Practices • Super Admins • Packages https://laravel.com/docs/authorization 35 Find me around the conference, and let's talk about auth! @DrByteZC @drbyte drbyte.dev Chris Brown 36 Laracon EU 2019 - Close the Gate! Laravel Authorization.key - August 29, 2019