Lesson Description

The "Migrating to CASL" Lesson is part of the full, Permission Systems that Scale course featured in this preview video. Here's what you'd learn in this lesson:

Kyle introduces the CASL authorization library, covering installation and refactoring existing permissions to follow CASL's subject-then-action convention. He then demonstrates defining AppAbility types in TypeScript and using AbilityBuilder to configure abilities and subjects.

Preview

Transcript from the "Migrating to CASL" Lesson

[00:00:00]
>> Kyle Cook: Now, like I said, we're going to be implementing CASL, and that's pretty much what we're going to be doing in this next section right here. So the very first thing that we're going to want to do is we're going to install the CASL library. And to install that, you just install @casl/ability, so we'll copy that over, go into our terminal, and we'll paste that down to install that particular library.

[00:00:17]
And you'll also notice that we're going to be using the types from this @ucast/core library. The only reason we're using these types is because we're going to be converting from a CASL permission to a Drizzle database query, just like we are doing in our system. So this allows us to bridge that gap. So we want to make sure we install this @ucast library as well. So we'll install both of these libraries so we can use CASL, as well as the types associated with it for doing that database conversion.

[00:00:44]
Now the next thing we need to do is to update all of our code to use this CASL library, and the biggest difference between CASL and the system that I built for this workshop is that CASL essentially flips the order of everything inside their permissions. So when we call builder.allow, we would put the resource first and the action second. In CASL, it goes action first, then resource second. Same thing here, we did our conditions first and then our fields second.

[00:01:10]
In CASL, it goes the fields first and then the conditions second. So there's just a flip-flop on the order that they do things inside of CASL. Otherwise, you can see the code is pretty much exactly the same between the two. Also, when we set up our types for this, you'll notice this is pretty much all the TypeScript setup we need. It's much shorter than what we do when we actually use CASL or when we wrote our own system, which required hundreds of lines of TypeScript as opposed to, you know, 10 lines of TypeScript.

[00:01:37]
Also, when we're using CASL inside of our different components, it's going to look very similar as well, with some small modifications. In our system, we pass along the resource, the action, and then all the data associated with that resource, and in CASL, they flip-flop it again, they pass along the action. They pass along the resource and the data associated with that resource, and they wrap it inside this fancy subject function, which just handles some stuff behind the scenes for CASL.

[00:02:03]
Not really important to understand what it does, it's just behind the scenes mapping things together for the library to work properly. But again, you can see it's the same data just being passed in a slightly different order to what we did in our system. Now, let's go ahead and actually implement this to see what it actually looks like inside of our code. So the very first thing we want to do is I'm going to take our attribute-based access control file.

[00:02:26]
I'm going to rename this to CASL, just so we know we're working in the CASL system instead of that attribute-based access control system that was custom written for us. Now let's scroll all the way to the very top, and let's work on getting all of our types set up for this system. So, we already kind of previewed what the types look like, I'll explain a little bit what's going on, but inside of TypeScript, they have this thing called abilities, and an ability is essentially what defines all of your conditions and how they're going to match up to each other, and each ability requires you to pass along the actions as well as the subject that you're going to be performing those on.

[00:02:57]
And in their case, they use the word subject to refer to what I've been calling a resource. So in CASL, when you see subject, that means resource in what we've been talking about throughout this whole workshop. So we essentially need to set up our relationship between our resources and our actions, which is something we did in our system, we just now need to do it in the type system for CASL. So let's create our project subject, and again, subject means resource, but CASL refers to them as subjects.

[00:03:23]
And in CASL, you need to have two different types of subjects. You need a string version, which is used when you check general permissions, and then you need an object version for when you want to check specifically though they have permissions for this exact project or this exact document. So we pass it along our string, whatever that string is going to be, in our case, we'll just call it project, and then we need to pass it along the actual condition that we want to check for this project.

[00:03:46]
Now the nice thing is since we already have our system built up, we can actually scroll all the way down to where we defined this originally. We can take our conditions from here and use them exactly as is because they're the same between our two systems. This is just mapping between them. So that creates our project subject. We can do the exact same thing for our document, we of course need to create a string associated with that, and we need to create essentially all of our conditions.

[00:04:10]
And again, if we just scroll a little ways down in our application, we can find where we defined our condition originally. These map one-to-one to what we have inside of CASL. So now inside of CASL, I've created what the stringified name of this thing is going to be, and I've also created what all the conditions I want to check for each one of these different values is. The next thing I need to do is I essentially need to combine these together with their action.

[00:04:33]
So we can come in here, we're going to create a type. I'm going to call this my ability. This is just a custom type we're going to create to use everywhere inside of CASL, and this is going to be equal to MongoAbility, which is something that comes directly from CASL. Let me make sure I get this imported properly because it doesn't look like it wants to auto-import. Get that spelled properly. There we go.

[00:05:02]
And you do see that the name says MongoAbility, so you immediately probably think, oh, we have to use MongoDB. I don't like MongoDB. Don't worry. All this is saying is that they're using MongoDB's query language for defining conditions. So our conditions can only be equal. That's the only thing we have in our conditions. But with this, you could do not equal, you could do less than, you could do greater than, all those additional things.

[00:05:23]
So this just defines how you write the JSON for your conditions. And I'll show you what that looks like as we start to write out all of our different conditions, but it doesn't mean you're restricted to MongoDB. Don't worry about that. We're still going to be using Postgres and normal SQL for everything. Now inside of here, we need to pass it essentially a union of tuples, and these tuples are going to contain our subject and our actions.

[00:05:52]
So, we need to first pass along our list of actions, so in our case, create, read, update, and delete. And then the second value we pass to this array is going to be the subject associated with that. So let's say we have our project subject just like that, and we can pass this down as a union. So let's do the exact same thing, and this one is going to be for our document subject. There we go. And again, if we wanted to make it so that you couldn't delete documents, just remove that from there, and now you can only create, read, and update documents.

[00:06:20]
Just like our system, it's the same thing, just written in a slightly different way. So now we have essentially all of our resource types or subjects as CASL calls them. We have our actions associated with them. Now we just need to hook this up into their own permission builder, because obviously we can't use our custom-built permission builder, we need to use theirs. So to do that, inside of here, instead of creating this new permission builder, we're going to create a new ability builder.

[00:06:44]
They call permissions abilities in CASL, so pretty much if you see the word ability, it generally means permission as well. Now, this new ability builder, we need to specify the generic type, which is my ability. That's that custom type that we created. This is just what hooks up all of our TypeScript fanciness for us. And then we need to pass it how we want to define our abilities. In our case, we're using that MongoAbility, so we can pass along createMongoAbility, and that just makes it so that we can use that MongoDB syntax for querying our data when we're doing our different conditions.

[00:07:14]
Again, I'll show you what that looks like once we get to that particular point. Now instead of returning to us an object that we need to use, this is actually going to return to us some different functions we can extrapolate out. So we have a build function just like our system had. We also have a can function, which I'm going to rename to allow to make it easier to understand in our system, because as you can see in our system, we used allow for pretty much everything.

[00:07:37]
So we're going to use allow as the name for our can function here as well. Now here we can just say build. If we don't have any user, we'll build and they'll have no permissions, just like before, and down here we can call build just like that. So that maps our build functions. All we need to do next is to use this allow function. And to give you a little bit of an example of how this looks, we can say allow, and the first thing we pass in is our action.

[00:08:00]
So it's reversed from how we originally wrote it. So let's say we want to allow a create, then we pass it in the resource, let's say document, and then we can pass along any fields that we have, or if you don't have any fields, you can just skip straight to conditions. They have a little bit of a function overload on this. So if you have no fields to restrict, you can just go straight to conditions.

[00:08:18]
And now we can specify whatever conditions we want. Let's say that we wanted to have a status condition here of draft that would check that draft status condition for us automatically in this section. Now, the nice thing is, like I said, we can use this with like MongoDB stuff, so we could come in here with like not equal, I believe is something you could pass in, and then we could say status draft, and this would get you everything that's not a status of draft.

[00:08:41]
So that's an option for this particular thing. I don't know the exact query syntax off the top of my head because it's not something we're going to be using in this workshop, but everything that's available in MongoDB syntax query, you can use since we're using that createMongoAbility. Now that's essentially how you create these. So let's go ahead and actually use them for our permissions as is. So we can take this allow function and instead of passing a builder along, we're going to pass this allow function into each one of our different permission builders so we can call that wherever we need to use it.

[00:09:12]
And I'm also going to create a custom type for that. We're going to call this our allow function. And this is equal to taking our ability builder, which is our custom type of my ability, and I just want to get the can function from there. I'm just creating a helper function so we can use this in all of our different places that we have this. So our add admin permissions, instead of a builder, this is going to take an allow function which is of that type allow function.

[00:09:35]
And now all I need to do inside of here is essentially just call the allow function and swap the order of everything because of course in CASL the order of everything is swapped. So it would go read first and then document too just like that. That would allow me to read all documents inside my entire application. Now there is another nice little helper that you can do inside of CASL. We can actually, I believe, change this to manage and this to all.

[00:09:58]
Actually, it doesn't look like it's quite working for us, but I believe inside of CASL there is this manage all, which is like a custom type that you can use for managing all different things. For some reason, I'm getting TypeScript errors on it, probably because of how I set up my permissions up here, but just know that this is a thing that you can do. But for now, we're not going to worry about that.

[00:10:24]
We're just going to hard code every single one of them as is, so we could say read, update, delete, create and we'll do the exact same thing with project as well. There we go. So now we have all the properties just like we did before. Essentially we just swapped the order of them, but that's all we need to do. We can do the exact same thing down here for our editor. So come in here, this is our allow function.

[00:10:55]
And instead of using our builder, which is called allow. And we will swap the order of these. So this is going to go after. There we go. And those are cleaned up, and let's do the exact same thing here for our document. And the exact same thing here. There we go. And it looks like we have a bit of an error. Oh, it's because I need to swap the order of these because again, CASL swaps the order of pretty much the exact way that we did everything.

[00:11:39]
Now our editor is completely done, our admin is completely done. Move on to our allow function here. Move our project over one. There we go, and exact same thing here. We'll just need to swap the order of everything, unfortunately. So we'll take the document and we'll swap that over by one. You should do it all at once. There we go. And anything that has an array, of course, needs to be the last value in the array, so let's just do that.

[00:12:24]
And we'll move this one up as well. There we go. That's all of our author permissions. And then finally, we'll do our viewer permissions last. So let's make sure this is that allow function. And allow swap the order of these. There we go. And then finally swap the order of these as well. There you go. So that's all of our permissions being entirely defined using CASL instead of our own custom permission builder.

[00:12:50]
And if we look, I think the only errors that we're going to have inside of here are maybe some of our permission builder stuff, but you'll notice we're not using this anywhere, which is great. So let's scroll back up to the very top here. Let's go back to our reference here. So we have our type setup completely done. We have our permissions completely done. The next step is to actually use those permissions inside of our different components to actually render out what we're supposed to do, because right now we're still using our permission builder everywhere, so you can see everywhere in our code we have tons of different errors because everything is essentially in the reverse order of what it should be.

Learn Straight from the Experts Who Shape the Modern Web

  • 250+
    In-depth Courses
  • Industry Leading Experts
  • 24
    Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now