Permission Systems that Scale

Permitted Fields with CASL

Permission Systems that Scale

Lesson Description

The "Permitted Fields with 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 builds a "pickPermittedFields" helper function using CASL's built-in utilities, then integrates it into the create and update document services to replace the custom implementation, verifying that unauthorized fields like "isLocked" are properly stripped from requests before hitting the database.

Preview

Transcript from the "Permitted Fields with CASL" Lesson

[00:00:00]
>> Kyle Cook: So let's go over to our CASL section. Here we go. I want to scroll all the way down to the code that we have here. We can actually clean a lot of this up in a little bit, but I want to create a brand new function called pick permitted fields, just like that. We're going to export this as an asynchronous function, and this is just going to be a helper function we can use that'll work exactly the same as our pick permitted fields function inside of here.

[00:00:25]
It's just going to be using CASL instead of our custom implementation for everything. So this pick permitted fields is going to take in a generic type. This generic type is going to extend a record of string and unknown because we don't know what the type is going to be yet. So essentially it's just saying we have an object of unknown type, that's what our generic is. Then inside of pick permitted fields, since we're using CASL, we need to use our action and subject in the reverse order, so we will use our action first, and to get what that action is, we can actually get that from the fields related to our my ability.

[00:00:59]
So if we scroll all the way up, this my ability we created, you can see this has all the actions we have access to directly inside of it. So what we can do is we can say here that the parameters of the type of permitted fields, which is a function built into CASL. There we go. I'll need to probably import this because it doesn't want to auto import. So we come all the way up here where we have CASL being imported, and we can import the function permitted fields of permitted fields.

[00:01:38]
Actually, it may be coming from a different location. So let's see, permitted fields of from @casl/ability, I believe. The reason we're getting an error is because I have this in two places. I was importing it from the right location. Okay. Now that we have that in place, we can actually write the actual function itself. Just give me a second here. There we go. So we have a built-in CASL function for getting the permitted fields.

[00:02:01]
What I essentially want to do is I want to get the parameters from them, so we can say my ability. That's our custom type for everything, and I specifically want to get the second parameter from the permitted fields of. The second parameter is the action when we're using that inside this context. Let me clean this up a little bit so we can get some. There we go. Now I want to get the second parameter of that, so that should be outside my parameters generic.

[00:02:26]
So what this is going to do is it's going to check all the parameters of this function. If we look at this function, the second parameter right here is the action. The third parameter is the subject or resource as we like to call it. So to be able to get the subject, we can get the third parameter from this function, and that'll give us the subject. And finally, the data we want to filter is just that object of unknown type that we know nothing about.

[00:02:51]
We want to filter it down to a type that we can use. Now what we can do is we can get our permissions, which is just await get user permissions just like that. Then we can get all the fields that we have access to by using that built-in function. So the nice thing is they have this function built in for us. We can pass it in our list of permissions, we can pass it in our action, and we can pass it in our subject.

[00:03:14]
And finally, we need to pass it in an object here, which is options, and this options has a fields from rule on it. This takes a function that we pass along to it, that gives us a rule value from it. Now, all we want to do is we just want to get the fields from this. So we just say rule.fields. So a rule is essentially all these things we defined up here. I just want to get the fields from my rule, and this tells me what fields we're allowed to actually pick our data from.

[00:03:40]
If for some reason there are no fields defined, we want to be able to get all of the data back as is. So we'll fall back to Object.keys of our data. So essentially what this says is if we have defined specific fields we have access to, use those to restrict what the fields we have access to are. If we have not defined any fields yet, this is null, then just use all of the keys of our data, which essentially means don't change what our data is, return it without any modifications at all.

[00:04:09]
So this will give us an array of all the different fields we have access to. So we can just do our for loop just like we did before. So I can get our results by calling this a record, which is a string and unknown because again, we don't quite know what type this is because our generic type is entirely unknown. Default that to an empty string, and then we can say const field of fields. These are all the fields the user has access to, and we just want to take our result data.

[00:04:41]
Set the field equal to the actual data value for that exact same field. Finally, we can return our results here, and I'm actually going to change this to a type of partial of T, just like that, because we know that whatever object we pass in here, the resulting data we get back is always going to be at least as small as that object. It'll be that object or smaller because we may not pass along all the fields.

[00:05:03]
So this code does the same thing as our pick permitted fields function does. It's just using the built-in function from CASL, and then it's just taking those fields and narrowing down what the fields are on our particular object. So now, anytime that we have pick permitted fields being used in our code, for example, right here where we have that to do, we can just replace that with that helper function we created.

[00:05:26]
So we can use pick permitted fields. We know in our case that the create or action should go before, so we have our action, then our actual resource, and then finally our data just like that. There we go. That's everything we need to do to get our restricted fields. That should solve that problem. Now we can do the exact same thing in our update as well. So let's uncomment this code. Remove the to do comment.

[00:05:48]
Make sure we pass along our update first, and then we need to wrap all this in a subject because that's the way that CASL expects us to go. So our subject is a document, and we'll spread out our document object and then pass in the actual data that we care about, just like that, and that should solve all of our restricted field related problems that we were dealing with. Now I don't believe this is being used anywhere else yet.

[00:06:10]
It looks like it's just in this particular file. So let's go ahead and actually test this to make sure it's working properly. I'm going to create a new document, pass it along some random data, and of course we are getting a particular error, says invalid data, so it looks like something's not being passed up like we expected it to. Let me just double check to make sure I wrote all this correctly.

[00:06:35]
I believe it is mostly correct. Let us see here. Oh, it looks like I'm using the wrong document here. This should be. Wait a second, this is a promise. Let's make sure we await that promise and do the exact same thing down here. That should solve all of our problems. Now when I click create, the document does go through, and to make sure it's actually restricting our fields, we're actually going to revert a small change in our UI.

[00:06:57]
So let's go to our document new page and we'll say that is locked. Let's just say that we'll change that to true. So we're letting them see that field when they go into the page. So if they create a new document, you can see I can toggle that is locked field, but when I save, it'll properly remove that. So you'll notice there's nothing saying that this document's locked because it's not allowing that to be passed on.

[00:07:16]
So at least we know our code is working like we expect it to. Let's just change back that permission so that we don't have that showing up like we don't want it to.

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