Lesson Description

The "Field Level Read 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 implements field-level permissions into the ABAC system, extending the TypeScript types and "can" function to optionally restrict which document fields a user can access, then demonstrates it by hiding the "created at" and "updated at" fields from viewer-role users.

Preview

Transcript from the "Field Level Read Rules" Lesson

[00:00:00]
>> Kyle Cook: Next we're going to work on implementing field-level permissions, which unfortunately is going to require a little bit of extra TypeScript code for us to write. So essentially I want to be able to expand my can function for my permissions to not only take in the resource, the action, and the conditions, but also individual fields that I may want to read or update in my application. For example, can I read the is locked field or can I update the status field?

[00:00:22]
So what we need to do is essentially update what our policies look like, so we can pass along an array of all of the different fields that they have access to if we want to restrict it. And just like with conditions, this entire thing is optional. So if we don't pass any fields along, we just assume they have access to every single field, but if we do pass along fields, we restrict them to accessing just the fields that we pass along.

[00:00:44]
Now what we need to do is go ahead and implement that. So first we need to add field-level permissions to our allow function to allow us to restrict them. We then need to update our can function to check these actual permissions, and finally, we need a way to be able to filter all of these to get just the fields the user has permission to access or not. So let's go ahead and work on actually implementing that.

[00:01:03]
So in our attribute-based access control, what we need to work on is the type-based section down here, so we have our resources. This is where I want to define what all of my different fields are going to look like. I'm going to be defining this as a property called data. Just like that, this is just going to be my full object. So every single field that my project has name, ID department, and so on, those are all fields that I could possibly restrict on.

[00:01:24]
You can make this more restrictive if you want, but I'm just going to put in all the fields possible. Same thing with my document, I'm just going to set my data to be everything. So let's get our document just like that. We're saying that all of the possible fields are what our data for our document is. Next, what I want to do in my permissions, we know that this is essentially a representation of our can function or our allow function, sorry, we want to specify the fields that we can allow our users to see or not see.

[00:01:51]
This is again going to be an optional parameter because if we don't pass this along, they can just access all of the fields, and this essentially is going to be a key of my resource. So and get the specific resource we're looking at, and I want to get that data. So this is just one of the keys of, for example, document which would be ID created at updated at project ID and so on. Now the next thing that I need to update is my allow function to take in those fields as well.

[00:02:19]
And in our case, it's going to take in our fields just like this, and that's going to come from that nice little permission helper we created, so we're going to get our response, and we're going to get specifically the fields from there. And again, this is an optional parameter, so we'll make sure it's optional, and we'll pass it along into here. So essentially all we've done is we said, OK, here is what all the fields for each of our different types can be.

[00:02:42]
We've then said we want to pass along all of the different fields we want, and this should specifically be an array of data. So we're passing along an array of all the fields we can access, and then here we're passing that array into our allow function and just making sure it's included with the rest of our permissions. Now before we start finishing our can function to include all that, let's just go up here and see how this looks inside of our application.

[00:03:01]
So let's go ahead and we're going to limit down our viewer, for example, because we know our viewer can only read certain fields of a document. So what we can do is we can pass along a new parameter in here that is an array containing all the fields they have access to, and you can see we get type safety for all of this different stuff. So let's just list down all the fields that they have access to.

[00:03:22]
They can access the content, they can access the ID. They can access, for example, the creator ID, they can access the is locked property. The last edited by ID project ID. Status and they can access the title. I mean they can pretty much access everything, so we have a list of all the things that they have access to, and we're just ignoring the two that they don't, which is created at and updated at.

[00:03:47]
We can then copy that down to this other allow because these are both for allowing the user to read documents, so we need to put this in both places. Now you may think this is a bit of a downside because right now we have to copy this array across two different places, and I do agree with you with that. The reason we're doing this is because we're keeping our conditions very simple. We could add more complicated conditions like not equals or in array or things like that, but that would require a lot more code on our conditions side, and you'll see when we start talking about using libraries to implement some of this, they handle a lot of that grunt work for us because that's a lot of complicated TypeScript code that really would bloat the size of our project.

[00:04:22]
So for now, we're just going to copy that array down into both different sections. So now we've restricted what actually data can be viewed by this particular user. Now we need to make sure that inside of our can function, we also filter what that data is going to look like. So let's scroll all the way down. Inside of here we have our can function, and if we look, the way we want to use this can function is we want to pass it a single field to check against.

[00:04:46]
So we're checking can they update a document with these particular attributes and we want to check can they update this field in particular. So instead of passing an array of fields, this is going to take in a single field, and again it's going to be entirely optional if we pass this field in or not. Now this field is going to be a key of coming from our resources. It's going to get that data property that we just defined, and again, we only want the keys of this particular property because we just want to know, are they accessing the ID or the created data or something along those lines.

[00:05:17]
Now here we are already checking our valid data, and if our data is invalid, we should probably return false, so we can just say here, if we do not have valid data. Then we can just return false. Then we can check to see if they have valid fields. And luckily this check is actually quite simple. So we say const valid fields. We'll just say a valid field because we're only checking one field. If the permissions do not have any fields, if that's equal to null, well then we return true because we're not filtering the fields at all.

[00:05:50]
If we're not checking any field, again, we return true because we don't care if they have field access or not, we're not limiting them on that. And finally, if we are checking for a field, we just check the permission and see if any of the fields includes the field we're checking for, because that means they have access to that field if it's in that array of fields. Finally, we can return. Are valid field just like that, and this will be true or false if they have access to that field or if they don't.

[00:06:19]
So now let's actually use this inside of our code. We know that inside of our document, we can go to the view page for our document, and that is where we actually show the created at and updated at time. If we scroll all the way down here, you can see created at and updated at. This is where I want to do my permission check to see if they have access to this information or not. So let's scroll all the way to the top.

[00:06:41]
We do have our permissions, so we can just use those directly in line. Again, this is where we're using that information. I'm going to wrap all of this. Just like that, and I'm going to write my permission check. In our case, our permission check is going to be permissions.can. We want to check on the document to see if they have read access for this particular document that we're looking at, and we want to check again, nice autocomplete based on what we set up.

[00:07:05]
We want to check to see if they can look at the created at field specifically. If they can, then we're going to render out the div that contains all that information. And we're going to do the exact same thing with this div here, make sure we wrap all of our code properly. There we go. And this should be updated at. There we go, so now we're checking do they have access to that updated at field, and we should immediately see this take effect in our application.

[00:07:33]
So right now we're logged in as an editor, so editors can see the created at and updated at field, no problem at all. Now if we log into a viewer instead and we were to try to access this, you'll notice those created at and updated at fields have been completely hidden because we no longer have access to those. If I were to modify my permissions, let's say, scroll all the way up to where we had those permissions being defined, and let's just say that I added created at into here.

[00:07:56]
And we add it to both of them because again, we have this in two different arrays. If I come back, you can see the created at field has now popped up. So it's very easy for me to make these changes. The one downside to field-level permissions is it makes your actual code where you use the permissions more complex, because now you need to wrap all your different fields in a check to see if they can view that field or not.

[00:08:17]
So this is something that I only recommend adding field-level permissions to your system if you really truly need it in your application. If you don't need it, it's a very big step up in complexity for sometimes a very marginal gain in actual things inside your application.

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