Lesson Description

The "Field Level Write Rules" 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 out the write side of field-level permissions by creating a "pickPermittedFields" function that filters submitted form data down to only the fields a user is allowed to modify, then defines specific field restrictions for authors and editors in the permission configuration.

Preview

Transcript from the "Field Level Write Rules" Lesson

[00:00:00]
>> Kyle Cook: Now let's bring this back to where we had it before, where they were limited on that created at field. There we go. And we have essentially solved the problem of implementing the read version of our field permissions. Now we need to work on the write version of our field permissions as well. So that's going to be a little bit trickier, and that's where this third field comes in with adding a way to filter all of the permitted fields they have access to.

[00:00:21]
Essentially, I want to pass in an object with all the things that they want to do, and then I want to filter that to only the fields that they have access to actually modifying inside of our code. So just give me one second here. There we go. Perfect. So, let's go over to our attribute-based access control. We're going to scroll all the way down into build, and we have our can function. I want to create a brand new function, and this function is going to be called pickPermittedFields.

[00:00:51]
Now this pickPermittedFields function is going to be very similar to our can function, takes in the exact same generic as everything else we've worked on based on responses, or sorry, resources. It's going to take in a resource of that resource type. It's going to take in the action that we're wanting to perform. Again, very similar to can. I can actually copy this because it's going to be a lot of the same code.

[00:01:12]
So we're taking in that action, which is the exact same action that we were looking at before. There we go. The next thing that we want to take in is going to be the new data. So this is if we're trying to update a document, this will be the update data we're trying to save to the database. If we're creating a new document, this would be the new document data we're trying to save in the database. So we need our new data right here, and this new data should match our condition exactly.

[00:01:38]
And then finally, what we need to do is we need to add in what our existing data looks like. So if we're updating a document, the first thing we pass into new data is the new data coming from the form. The second thing we pass in is going to be the actual data of the existing document they're trying to update. So these are both going to match the exact same thing. These are our condition sections right here, and this one right here is going to be our data.

[00:02:03]
There we go. So that is everything that we need to create this particular function. Now what we need to do is we just need to loop through, find all the fields that the user has access to, and essentially return a limited version of our new data that only contains the fields that they have access to, because we just want to limit what they are able to update inside of our code. So the very first thing that I want to do inside of here is I want to get all my permissions for a specific resource.

[00:02:33]
I want to filter these permissions. There we go, and I essentially just want to get the permissions that apply to this combination of resource and action that we're currently looking at. So if our action for permission is not equal to our action, we'll just return false because they don't have access to this particular action, so we're just limiting again our permissions based on what we have access to.

[00:02:56]
And then I'm just going to copy this code over. If you are writing this from scratch yourself, you may want to not copy this, but I'm just going to copy this code because we need to check to make sure that all of our data for our conditions matches up. So we're going to take all of our condition code, paste that into here to make sure we have valid conditions for everything, and then we can just return valid data just like that.

[00:03:15]
Again, if you're writing this from scratch, it'd be better to write clean code, maybe separate this out into a function, but for example use case, this is going to work fine. So this right here is going to give us an array of every single permission that applies specifically to this user for the thing that they're trying to work on, and it's only the ones that are returning true essentially. So what we want to do is from this list of permissions is get all of the fields they have access to because they could have different field access on different permissions.

[00:03:41]
We want to combine them all together into one single array of fields that they have access to. So very first thing is if the permissions.length is equal to 0, well, that means they don't have access to this at all, so we can just essentially return no data at all, or return an empty array or not an empty array, an empty object because they don't have permission, so they have no fields they have access to.

[00:04:05]
Next thing that we can check for here is to get the unrestricted access. Restricted, there we go. So we can take our fields. We want to filter this. Oops. And we specifically want to filter it where our field is equal to null. So this essentially means is there at least one thing inside of our list where they have access to all the fields, because if we don't pass along any fields, by default they have access to all the fields.

[00:04:35]
So this is checking, does one of the permissions have that particular qualification. So if our unrestricted.length is greater than 0, that means at least one permission has no restriction, so we're going to return the data, not restrict it at all, because they have full access to every single field of our data. So this first case is saying you have no access, the second case says you have full access to all the fields.

[00:04:57]
Now the final case that we want to talk about is going to be the actual last case of which fields do you actually have access to. So we're going to create an array here called permitted, which is just going to be mapping over all of our different fields, so we can say perm.fields, oops. There we go, get all the different fields, and we're just going to default that to an empty array. Now, in our case, we shouldn't have a scenario where we run into this, but TypeScript doesn't know that these shouldn't be null, so we'll default it to an empty array, and now we get all the fields that we have permitted access to by combining those all together into one single array.

[00:05:44]
Now if for some reason, this is equal to null, we can just return here our new data. And then finally, we can actually limit what our object looks like. So I want to get a result object. A result object is going to be a partial of our resources, just like that. And we specifically want to get the data field from it. There we go. Looks like I might have a typo in here. Ah, yes. There we go. That's giving us a result object.

[00:06:16]
So essentially you notice our new data is the data that we want to restrict, and this is just giving us a restricted version of that. We're essentially going to be mapping that new data to this new result object, but only showing the fields we have access to. So we can do a simple loop where we loop through all the fields in our permitted fields. There we go. And for each one, I just want to take my result for that field, and I want to set it equal to my new data for that field.

[00:06:49]
Essentially, I'm stripping back what fields I'm allowed to add into this result array right here. And then finally, we can just return that result array down right there. Now I should set this to an empty object to start out. There we go. Looks like we have a little bit of an error inside of our code, and make sure I don't have something incorrectly typed, of course I do. I swapped the data and condition on these.

[00:07:12]
There we go. So that solves our particular problem we're running into because our new data should be a full object of all the data we want to update. And then finally, our data that we're checking against should only be the conditions that we care about. It looks like we still have a bit of an error. Let me just double check my code to make sure I don't have any TypeScript stuff going on because that's probably what the problem is.

[00:07:34]
Oh, of course. I think I want to make sure that I type what this function is going to return just so we get proper type safety. I don't think this will solve our problem, but it is something we want to do. This is going to be a partial of our resource, essentially the same type as like result data. There we go. Make sure I spell that properly, so we're just saying we're returning a partial data back down to the user.

[00:08:01]
Looks like I'm still getting some errors. Let me just double check. For some reason, when I copy paste my code, this is my problem. My new data is optional, obviously not supposed to be optional. If I remove that, there we go, I cleaned up all of our different TypeScript errors. So this is a lot of complicated code to essentially create a function that we pass it in a data object and it's going to return to us a new data object with only the keys that the user has access to.

[00:08:30]
So what we can do is we'll scroll up to our code where we want to define what fields an author can edit, because as we know, if we look here, we want to restrict the author to only be able to edit content and title, and editor can only edit title, content, and status. We want to restrict this down on our page, so inside of our author permissions, anytime that we want to create or update something, we want to limit them to only updating specific fields.

[00:08:57]
So in our case, content and title, just like that. Same thing for update, we want to make sure they only update the content and the title of the document. Now our editor is going to have very similar permissions, but slightly less restrictive, so inside of our update section, they can get the content, they can do the title, and they can do the status. There we go. So those are the different fields that they can update inside of our application.

[00:09:20]
So now we can actually use our code to be able to show the fields in our form, and then when we get our data back in the form, we can use that pickPermittedFields function we just created to essentially take the data they send us and limit it to only the fields that they have access to. That way they can't circumvent any UI restrictions that we have in our code.

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