Ready to get started?

Join Adocasts Plus for $8.00/mo, or sign into your account to get access to all of our lessons.

robot mascot smiling

Rolling Our Own Authorization Access Controls

In this lesson, we'll create our own simple authorization access control list. We'll then share this list globally throughout our application by appending it to our HttpContext and sharing it with our Vue application via Inertia.

Published
Jan 31
Duration
6m 55s

Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.

Adocasts

Burlington, KY

Get the Code

Download or explore the source code for this lesson on GitHub

Repository

Ready to get started?

Join Adocasts Plus for $8.00/mo, or sign into your account to get access to all of our lessons.

Join The Discussion! (2 Comments)

Please sign in or sign up for free to join in on the dicussion.

  1. Commented 4 days ago

    Hi again Tom !

    Sorry I ask a lot of questions 😅

    I was wondering how I could implement a more granular permission system where a user can have different roles at different levels - for example, being an admin at the org level but only a member for a course and an admin for another one within that workspace. First I would have to create another action GetCourseAbilities and create "rules" like we did for GetOrganizationAbilities .

    My problem is to understand how to modify the middleware to get the roleId associated to the course. Since we only get the roleId for the workspace. Should I create a middleware that specifically check the ctx.params to retrieve the course.id and find the user's role like we do in the workspace_middleware ?

    I should have tested it before asking you, but I have analysis paralysis and I'm not sure this is the best and cleaner idea.

    Also I was wondering how we can check more things than just the user's role for an organization's ability. For example, canceling an invite. An admin user can cancel all invites, but a member user cannot remove invites, unless he's the one who made the invite. Tried multiple things here but none of them is working. I think I didn't understand very well how things work 😅

    1

    Please sign in or sign up for free to reply

    1. Commented 3 days ago

      Hi Emilien!

      No worries at all!! 😊

      Yeah, things spiral here quickly depending on your approach. It's a big enough of a topic to be a series of its own, but I'll try to give an overview below.

      Access Determination

      So, the first thing you need to do is figure out how the more granular access will be determined. Will only the creator have permission to edit, update, and delete? Or, maybe the creator can invite others to edit, update, and delete? Essentially, how are the different roles going to be determined at those different levels?

      Creator Only

      If it's just the creator, then that's much easier. You'd want to capture the userId of the user who created the resource. For example, adding a ownerId onto the Course model. Then, you'd handle the logic very similarly to how we allow org members to remove themselves throughout the next two lessons (12.1 & 12.2). By giving priority to allow the action if the ownerId matches the authenticated user's id. When that's the case, you'd want to ignore whatever the role's permission says and allow the action.

      Resource Specific Invitations

      If you want to allow the creator to invite/share with others to collaborate, you'll need a table to track that. This table could look many different ways.

      • It could be a single table per resource, ex: course_user consisting of course_id, user_id, and role_id if there are different levels and not an on/off.

      • A polymorphic table, ex: permission_user consisting of something like entity , entity_id, user_id, and again role_id if needed. Here, entity would be the table/model and entity_id would be the row's id for that table/model. Now, Lucid doesn't support polymorphic relationships but that doesn't mean you couldn't specifically query for what you need. The downside is, that you can't use foreign keys for the entity_id since it'll be dynamic

      • A single table with a nullable column per resource, ex: permission_user consisting of something like course_id, another_id, user_id, and again role_id if needed. This is similar to the polymorphic relationship, but you have a specific column per resource needed. Plus here, is you can keep foreign keys. Downside here, is it can be a lot of columns if a lot of resources need to use it.

      There are many different ways authorization can look beyond these two, but determining what that looks like is the first step.

      Populating Permissions

      When it comes to populating these permissions, again, it's going to depend on just how granular you need to go. For us in this series, things were simple so our approach was simple. The approach covered here was meant mostly for a single set of roles that then trickle down as needed.

      If you're going to be listing courses and each of those courses in the list is going to have varying permissions based on invites/shares, then you might be better off moving the can approach to the row level. For example, allowing something like course.can.edit to determine if the authenticated user can edit the course. This could be done using something like model hooks.

      You could also always reach for a package to help achieve your implementation.

      That was a lot, sorry! Hopefully, that helps at least little though.

      1

      Please sign in or sign up for free to reply