Permission Systems that Scale

Resource & Permission Types

Permission Systems that Scale

Lesson Description

The "Resource & Permission Types" 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 begins building the attribute-based access control system from scratch, defining the core TypeScript types needed for the permission builder, including resource types, conditions, and a permission store that will form the foundation of the new ABAC implementation.

Preview

Transcript from the "Resource & Permission Types" Lesson

[00:00:00]
>> Kyle Cook: Now we're going to go ahead and actually try to implement out this system ourselves, and you'll notice the complexity is going to be much higher in this particular system than when we dealt with role-based access control, and a lot of that complexity comes from TypeScript itself because we need to write relatively complex types to make sure we get type safety for all the different things we do inside of our function.

[00:00:19]
So let's dive into our code, and the very first thing that we're going to do, go into our source folder, let's just close out all these files so we don't have anything open to distract us, and we specifically want to go into our permission section and we want to go down where we have role-based access control, and I'm just going to completely delete this file, we're not going to be messing with that file anymore.

[00:00:36]
We're going to create a brand new file called attribute-based access control ABAC.ts that is going to handle all of our permissions, and we're going to map these permissions 1 to 1 to what our existing application looked like, so we're going to see the exact differences between our role-based access control versus our attribute-based access control. Now the very first thing that I want to define is that permission builder that we looked at here, because this is where all of the complexity for our TypeScript is going to go.

[00:01:02]
Once we have this built out, you can see defining our actual permissions is relatively straightforward, but getting that set up is where the complexity comes in. So the very first thing we want to do is we need to define a type for all the different resources that we're going to be looking at in our code. So we're just going to call this resources, and this type is essentially just a helper type for us to map together what each resource looks like.

[00:01:23]
So we're going to give it a key of our resource name. In our case we have projects just like that, and we have documents, so we're going to do project and document just like that, and each one of these is going to contain an object that essentially just allows us to map between all the different things we can do with these. For example, I want to get all the different actions that are available for my project, so I'm going to put that inside of here.

[00:01:43]
So now we can write out all the different actions supported by our project. In our case, our document and project both share create, read, update and delete permissions, so we can just come in here with a union that combines together all those different permissions just like that, and we can copy that down to our document as well because both of them share the same permissions. If for some reason it was impossible for a user to delete a document, we could remove that and then we would get type safety between them, where we would only see the actions available for the document that it has.

[00:02:15]
The next thing that we need to specify are the actual conditions we want to check. You noticed in our old role-based access control we were checking for things like is locked in the status property and so on. This next property we're going to define, which is called condition, is just going to be essentially a type containing all the things that we care about checking. So we're going to use this pick property here in TypeScript that allows us to narrow down our type, and we're going to pick our project, so by default, if we didn't have a pick in here, it would give us all the properties of our project.

[00:02:43]
But in our case, the only thing that we narrow people based on our permissions is the department of the project. All we care about is checking if that's equal to the user's department or not. So the only condition I'm ever going to check is my department condition just like that. So let's make sure we get that imported, and now I've set up everything I need for my project-based conditions. Next, I want to set up my document conditions, so we'll come in here, we'll change this to documents, and what I want to do is I again want to check for all the things that I'm looking for.

[00:03:12]
In our case, I only care about checking the document's project ID because I want to make sure they have access to the project. I also care about checking the actual creator ID because I want to know if they own this document or not. I want to check the status, and I want to check if it is locked property as well. All things that we've pretty much looked at before in our role-based access control, I'm just making them super explicit here on what we do and do not have access to.

[00:03:37]
And again, like I said, these are just a helper type. So all we're going to do is reference this type in other types, and I'm just kind of putting this all in one place, so it's easy to centralize and look at. Now the next type we're going to be creating is called a permission type, and this type is just going to represent essentially each permission that we pass to that allow function, so it's a type that represents what the signature of this function essentially looks like.

[00:03:59]
So this permission type is going to be a generic that's going to take in a resource and that resource is going to be one of our resources, so we're going to say extends key of resources. So essentially this is going to be either a project or it's going to be documented as a string because that's the key of this particular object. Then, since, as I said, we want to essentially match this signature right here, we already have the resource that's inside of our generic type, we need to take in the action and the conditions that we want to apply to this, so we can get all those inside here.

[00:04:31]
We're going to get an action, and that action comes from this resources type, that's why we define that helper type for us. So this little bit of code right here essentially checks our resources type for the specific generic resource we have, so it's going to either give us all the data for our project or all the data for our document, and then we just want to get the action property from it, so it's going to be one of those actions.

[00:04:52]
Next, we want to get the condition, and we already know based on our sample code over here, this condition is optional. We could pass it along or we could pass along nothing in this case, so we want to make sure that in our code, it is an optional thing that we can pass along, and that's going to be coming again from our resources, nice little helper function or helper type that we've created. There we go, and we specifically want to get the condition from here.

[00:05:16]
Doing this though is not quite 100% correct, and that's because it requires us to pass along every single condition every single time we call this function. Instead, we only want to pass along a subset of the conditions, for example, just the status or the status in the creator ID. So to do that inside a TypeScript, we can just wrap this inside of a partial, and that's essentially saying that we can pass down any subset of our conditions, or since it's optional, we can pass down no condition at all.

[00:05:41]
Now the final type that we need to create before we actually start implementing the rest of this is going to be a type called permission store, and this permission store type is essentially where we store all of our different permissions, and we're going to store them in keys of either project or document. So this permission store is just going to be an object, and here we're going to have a little bit of fancy TypeScript.

[00:06:02]
I'll explain what it does once I finish writing it here. There we go, and we're going to be getting that permission type. So essentially what this little bit of code does is it's saying I want to loop through every key of my resources, which in our case is project and document, and I want to create a brand new object that contains those keys, and then each of those keys is essentially being assigned to an array of these different permissions.

[00:06:27]
So if I were to just create an object and I assign it that permission store type, you'll notice inside this object, I have to define my document, and it will be an array, and I have to define my project, which will again be an array, and inside that array, you can see I can define the action and condition which comes from this permission type right here. Now those are the base types that we need to get it set up, and it's okay if you don't understand 100%, which every one of these types do.

[00:06:51]
Once we start to use them, it'll hopefully be a little bit more apparent. I'll just shout out our TypeScript learning path for when people really want to get fancy with TypeScript and understand how to create more complex types. Got lots of courses there for them. Yeah, I would highly recommend that because there's definitely some complex TypeScript stuff that will be going on throughout this entire section, unfortunately, but it's kind of necessary.

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