Rbac Acl

By the end of this lesson you'll be able to tell authentication from authorization and build a real PHP authorization layer — roles, permissions, per-resource ACLs, policies with ownership checks, and middleware that enforces it all server-side, deny-by-default.

Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.

Part of the free Php course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

1️⃣ Authentication vs Authorization

These two words look alike and get mixed up constantly, but they answer different questions. Authentication is "who are you?" — the login that proves identity. Authorization is "what are you allowed to do?" — the permission check that runs after login, on every protected action. Being logged in does not mean you're allowed to do everything. Authentication always comes first; this lesson is about everything that happens next.

Notice the gate starts from "no" and only flips to "yes" when a permission is explicitly true . That is deny-by-default , and it's the single most important habit in this lesson — anything you forget to allow stays safely blocked.

2️⃣ The RBAC Database Schema

RBAC (Role-Based Access Control) connects three things: users get roles , and roles grant permissions . A user never owns a permission directly — they own roles, and roles carry permissions. Because a user can have many roles and a role can have many permissions, you join them with pivot tables (also called join tables): user_roles and role_permissions . Here's the schema and the single query that answers "can this user do X?".

3️⃣ Checking Permissions with RBAC

Now the PHP side. The whole point of RBAC is the single method can($userId, $permission) that the rest of your app asks. Role inheritance keeps it tidy: an admin inherits everything an editor can do, which inherits everything a viewer can do — so you define each permission once. Read how getAllPermissions() walks the hierarchy and how can() ends with return false (deny-by-default).

Charlie the viewer is denied posts.create not because of an explicit rule, but because nothing granted it — the loop finishes and falls through to return false . That is the safe default working for you.

4️⃣ ACL — Per-Resource Permissions

RBAC is broad: "editors can edit posts." But sometimes access is about one specific object — this document, shared with this one person. That's an ACL (Access Control List): a list of "user → resource → allowed actions" entries. Think Google Drive, where you share one file with one colleague as read-only. RBAC and ACL are not rivals; real apps use both.

5️⃣ Policies, Gates & Ownership Checks

A policy is a class that holds all the rules for one resource type (a PostPolicy for posts). A gate is the single yes/no question you ask it: "can this user update this post?" The killer feature is the ownership check : most apps want "editors can edit any post, and authors can edit their own ." That's a role-permission check OR an owner comparison — exactly the Laravel policy pattern, shown here from scratch.

Now you try. The can() below is almost finished — fill in each ___ using the 👉 hint so it allows only what a role grants, then run it and check the Output panel.

One more — this time the missing piece is the ownership check itself. Add the comparison so an author can edit a post they wrote.

6️⃣ Enforcing It with Middleware

All those checks are worthless unless something actually runs them on every request. Middleware is code that sits in front of your controller: each route declares the permission it needs, and the middleware blocks anyone who lacks it before the controller's logic ever executes. Return 401 when nobody is logged in (not authenticated) and 403 when they're logged in but not allowed (not authorized). This is your one enforcement point — the server, not the browser.

📋 Quick Reference — RBAC vs ACL

No code is filled in this time — just a brief and an outline. Write it yourself, run it on onecompiler.com/php or your own machine, then check your result against the expected output in the comments. This is the same write-run-check loop you'll use on every real authorization check.

Practice quiz

What is the difference between authentication and authorization?

  • Authentication is what you can do; authorization is who you are
  • They are the same thing
  • Authentication is who you are (login); authorization is what you are allowed to do
  • Authorization always runs before authentication

Answer: Authentication is who you are (login); authorization is what you are allowed to do. Authentication proves identity (login); authorization checks permission afterward — a logged-in user can still be denied an action.

In RBAC, how are permissions connected to people?

  • Users get roles, and roles grant permissions
  • Permissions are granted directly to each user
  • Permissions are stored in the session only
  • Each permission is a separate user account

Answer: Users get roles, and roles grant permissions. You grant permissions to a role, then assign roles to users; change the role once and everyone with it updates.

What database structures connect the many-to-many RBAC relationships?

  • Views
  • Triggers
  • A single flat permissions column
  • Pivot (join) tables like user_roles and role_permissions

Answer: Pivot (join) tables like user_roles and role_permissions. Because a user has many roles and a role has many permissions, pivot tables user_roles and role_permissions join them.

What does 'deny-by-default' mean for a permission gate?

  • Allow everything unless explicitly blocked
  • Start from 'no' and only return 'yes' when a permission is explicitly granted
  • Deny all logged-in users
  • Require two permissions for every action

Answer: Start from 'no' and only return 'yes' when a permission is explicitly granted. Deny-by-default means anything you forget to allow fails closed (a 403), which is safe; every can()/allows() ends with return false.

Why should you check the permission, not the role name, in your code?

  • Hard-coding role names means every new role is a code hunt; checking the permission keeps logic stable
  • Role names are slower to compare
  • Permissions are case-insensitive
  • Roles can't be stored in a database

Answer: Hard-coding role names means every new role is a code hunt; checking the permission keeps logic stable. Check capability (can('users.delete')) and let roles decide who has it, so adding or reshuffling roles never touches your controllers.

When is an ACL the right tool over RBAC?

  • When permissions group by job title
  • When you have only one user
  • When access is per specific object — this document shared with this user
  • When you never use roles

Answer: When access is per specific object — this document shared with this user. RBAC says 'editors can edit posts'; an ACL says 'Bob can edit THIS post' — use it for per-object sharing like Google Drive.

In a policy, what is an ownership check?

  • Verifying the user owns the server
  • Allowing a user to act on a resource they created, in addition to a role permission
  • Checking the database owner
  • Confirming the user is an admin

Answer: Allowing a user to act on a resource they created, in addition to a role permission. The pattern is 'editors can edit any post, AND authors can edit their own' — a role-permission check OR an owner comparison.

What status code should middleware return for a logged-in user who lacks permission?

  • 200
  • 401
  • 404
  • 403

Answer: 403. Return 401 when nobody is logged in (not authenticated) and 403 when they're logged in but not allowed (not authorized).

Is hiding a button in the UI enough to protect an action?

  • Yes, it fully blocks the action
  • No — anyone can still send the HTTP request; authorization must be enforced server-side
  • Yes, if the button is also disabled
  • Only if JavaScript is enabled

Answer: No — anyone can still send the HTTP request; authorization must be enforced server-side. Hiding a button is UX only; the server check inside the controller or middleware is the actual security boundary.

In the lesson's RBAC engine, how does role inheritance work?

  • Each role must redefine every permission
  • Inheritance is disabled in PHP
  • A role's permissions are its own PLUS everything it inherits, gathered recursively
  • Only admins can inherit

Answer: A role's permissions are its own PLUS everything it inherits, gathered recursively. getAllPermissions() merges a role's own permissions with those of the roles it inherits from, so admin inherits editor inherits viewer.