Lesson Description
The "Wrapping Up" 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 wraps up the course by comparing all the permission systems covered throughout the workshop against the original six goals, summarizing the pros and cons of RBAC, basic ABAC, advanced ABAC, and CASL, then closes with a Q&A covering topics like microservice policies, storing permissions in a database, and how the service layer applies to pure backend API projects.
Transcript from the "Wrapping Up" Lesson
[00:00:00]
>> Kyle Cook: Now, pretty much the last thing that we need to talk about is going to be a comparison of all these different systems and how they stacked up to one another, especially comparing our main goals at the start. We had six main goals we wanted to cover. We wanted to prevent unauthorized access, pretty obvious. We wanted a single source of truth. We wanted automatic enforcement. We wanted consistency between our front end and back end.
[00:00:20]
We wanted to never trust the client, and we always wanted to fail closed. All the systems we covered today automatically failed to closed, so we don't need to worry about that, but I do want to look at how they handled these other systems to see if they were better or worse than each other. Obviously our very first permission system before we even fixed anything failed in all of our categories. We didn't have proper authentication in all of our pages.
[00:00:42]
We obviously didn't have a single source of truth. There is no automatic enforcement, our front end and back end were totally different, and we trusted the client implicitly whenever they typed in a URL. We didn't have any checks for them. When we added in our basic permission fixes, this was before we added our services layer, we at least fixed unauthorized access, but we didn't really cover much else.
[00:01:03]
We also prevented people from going to a page with a URL. We prevented that, so at least we didn't trust the client, but we still manually checked stuff all over, we still didn't have a single source of truth, and it was really not an ideal system. All we did is take a broken system and make it not broken. Moving on to the services layer, this is the first time where we actually decided to take code and move towards a new system where we're like, okay, I care about how this system is constructed, and I want to make it as usable as possible.
[00:01:31]
And if we scroll down, you can see we started to check off some of our boxes. We still had our unauthorized access and never trusting the client handled just like before, but now with this service layer, we had a layer of automatic enforcement. As long as you called your code from the service layer, you knew that it was going to be automatically authorized and handled permissions 100%. We also started to move towards a single source of truth.
[00:01:53]
It wasn't ideal, but it was better because most of our code was in the service layer and only some of it leaked out into the front end and back end. Now we did have problems with consistency between our front end and back end because everything was copy pasted between them, so it was easy to make a mistake. Moving on to our basic role-based access control system, this is where we start to really see some of the benefits.
[00:02:13]
You notice almost everything is checked off. We obviously had all the same stuff as before, but the big thing is we pretty much had a full single source of truth. The only thing that was different was that we had helper functions that were added on top. So as our permissions got more complex, our single source of truth started to fracture a little bit, but for the most part, everything was in that single source of truth, and we had consistency for the first time between our front end and back end because we used the exact same function to be able to check our permissions in both locations, so there's no copy paste problems to worry about.
[00:02:43]
Now, when we started to reach the limits of role-based access control, we have the exact same results as before. It's just our single source of truth became even less centralized because we had more helpers and more fracturing. Now when we move on to basic attribute-based access control, this is where we really start to see a lot of the benefits. We again have all the same stuff we had with role-based access control, but now we have even better centralization of a single source of truth.
[00:03:07]
The only time where our source of truth was not a single source was our database queries duplicated the exact same permissions as our condition code in our permissions. So that's the only real downside to this basic system versus the more advanced one, but in many applications, that's a perfectly okay trade-off to not deal with the extra complexity of advanced attribute-based access control. Now moving on to advanced attribute-based access control, this is the first system where we truly met all of the requirements we came in with, the main one being that we now have a single source of truth that not only handles our front end, our back end, but it also handles our database to make sure everything works as we expect.
[00:03:43]
And since all of our code flows through that services layer, it's all automatically enforced for us. So really, if we change our permission, our entire application just propagates from that. Now, in the final branch, we moved over to CASL, and we essentially got all of the exact same benefits as our handwritten system, and that's because our handwritten system is heavily based on CASL. Really the only downside we got with CASL was we had to deal with some of their baggage, especially around types and classes, but we also didn't have to manage everything ourselves.
[00:04:13]
So, a really quick overview of the different permission models before we finish here. Role-based access control. The big benefit of this is that you mostly have a single source of truth. Your code is much easier to read than just ad hoc if statements everywhere. You get really good type safety for all your permissions, and it's relatively easy to update and change roles, especially if all you're doing is adding a brand new role or permission.
[00:04:37]
Now the big con is that as your permissions become more complex and you care about attributes, role-based access control really starts to fall apart, and it just makes it really difficult because your permissions start to explode in complexity. So really role-based access control is great for when you have a simpler role or a simpler app where all of your roles are very concrete. You have a small number of roles, and most of these roles don't care about attributes of the things that you're checking to determine if they can do something or not.
[00:05:02]
Obviously, once you have issues such as field-level permissions or more complex attribute-based access control, then you should move to a system that manages attribute-based access control. Now, the big pros here is we again still have one unified API just like before. Our conditions describe what's happening, so we have a much more declarative approach to our API. We don't have to worry about any helper functions, and it's much easier for us to add new things because we can just add a new condition or a new allow statement to really modify what some individual user can do.
[00:05:32]
Now, some big cons is that it's more complex to set up significantly because we have to create a whole permission engine and the TypeScript can be quite a bit of overhead. I recommend using this attribute-based access control for most applications. So pretty much anytime that you have to worry about attributes of something, this is kind of the step that you want to move towards, or if you need something like field level restrictions, this is going to be the perfect fit for that.
[00:05:56]
The only time I would avoid this is if you again have a really simple pure role-based permission system, then it makes sense to use just role-based access control. Now using a library, really the trade-off here is, do you care about the things the library sucks at, and are you okay putting up with that versus building out your own version that does everything you want, but then you obviously have to maintain it.
[00:06:17]
So the benefit of the library, obviously it's battle tested, it's a familiar API many people are used to, and in the case of CASL, they have a lot of additional MongoDB style queries that can make writing your conditions a little bit easier. Now the big trade-offs specifically for CASL is that class-based design, you have that React server component issue, and you also have less TypeScript safety inside your code.
[00:06:42]
Pretty much the, yeah, question? With libraries like CASL, do they provide built-in ORM query translation or do we always need to implement it ourselves, like in the example here? Yeah, so if you're using MongoDB or if you're using Prisma, I believe those are officially supported by CASL, but something like Drizzle, which is a newer tool, is not supported by default. I personally prefer using Drizzle, which is why I used it for this project.
[00:07:06]
But yeah, if you're using Prisma and MongoDB, those are officially supported. There might be some other ones, but those are the two I know off the top of my head are supported. So you do have that built-in functionality. But as you saw, writing your own functionality for that isn't massively complicated because they have that abstract syntax tree that does the complicated work for you already. But yeah, that is honestly another trade-off of CASL, is that if you want to have conversion over to your own actual, you know, database ORM depending on what database ORM you're using, you may have to write that yourself.
[00:07:38]
Now, the times to avoid CASL would be anytime that you don't need the complexities of attribute-based access control, or you don't need the more advanced features and your simple attribute-based access control is perfectly fine, or when you just don't want to worry about dealing with things like the subject class problems or the TypeScript safety that you don't get with CASL. And that's pretty much everything I've got for you.
[00:08:01]
I would say more often than not when you're starting a permission system, go with either a basic role-based access control or more likely just jump straight to the basic attribute-based access control. I know it's a slightly more complex system, but it scales so much better, and I find that the amount of time it takes to update a permission system is much larger than just the upfront time of building a slightly more robust one and then growing into it as you build your program.
[00:08:26]
So if anyone's got any questions, now is the time because that's pretty much all I got. Dustin, what are your thoughts on using an independent language for policies rather than a full-fledged microservice? Does the latter have some benefit that might be needed in certain cases? The benefit of using an independent language is it works across every single, you know, stack that you can think of. And if you're using a microservice, the nice thing is you can write all of your code in that microservice in your language of choice, you know, using CASL or whatever you want, and you can call out to that from other places.
[00:08:56]
But oftentimes, especially if you're doing like some UI related code on what you want to show and not want to show, that can be something that you really want to have a little bit more fine-tuned control over. And if you're constantly calling up to a microservice every time you want to show or hide something in the UI that can add a little bit of extra overhead. I know one thing that CASL can do that maybe your microservice can do as well, is it allows you to actually take your rules and compile them down to JSON, send that to your client, and then you can run CASL on the client, looking at that JSON and determine what to show or hide based on that.
[00:09:29]
So, if you have like a microservice that sends down some formatted data, and then you have a library for each language you care about that can parse that data and show or hide things, that's another option you can go. But I think it really depends a little bit. I think if you're going to be using that microservice approach where you're constantly sending up a bunch of requests asking to show or hide something, that can add a little bit of overhead versus having a policy engine that just returns to you the document of all the things they can and can't do, and then you just ingest that into your UI if that makes sense.
[00:10:05]
Did you have a question, Andrew? Yeah, I'm wondering, is CASL an acronym for something? I'm on the website right now on my second to access security language. Oh, is it really? Yeah, there you go. I think they just chose it because CASL sounds cool, but they decided to add an acronym to it as well. Any other questions? I know we covered like a lot in the last two hours, especially this by far the most complicated section.
[00:10:31]
So like if you have anything, even if it was from two hours ago, feel free to ask. Would you consider storing attribute-based permissions in a table in the database? Yes, so I was actually thinking about adding a section about having like a permission matrix where you can modify that and store it in a database or a JSON file or some other section like that. The only reason I didn't include that in this workshop is because it would double the length of the workshop because doing that is quite a lot of code.
[00:10:56]
It's not necessarily difficult. It's just a lot of code, especially to write the UI. But yes, one thing you can do with your permissions is store it in a database or some other format like that, maybe a JSON file, and then you don't actually need to redeploy your application to change your permissions because you just change them in the database or change that JSON file and then your actual code just looks up the database or that JSON file.
[00:11:17]
So that is definitely something I think is a great idea, especially if you have constantly changing permissions. If your permissions stay pretty much static all the time, that may not be as useful, but if you have permissions that can change rapidly, that is a really great way to solve that problem. This application that we built outside of the permissions, the actual functionality of like creating documents and whatnot, is that based on something or is it just a fully custom app that you know?
[00:11:46]
It's just fully custom. I mean, the app itself is relatively basic. I pretty much had AI help create like a shell of the application because the UI and the implementation of like how documents are created isn't really important. It's the permissions that I wanted to focus on, so if the code's really ugly, like I only kind of manage the AI and writing a lot of it, but the actual project itself is just kind of a custom thing that I pretty much was thinking, what is the best way for me to be able to add complex enough permissions without having a really big project, and this was the best way I could think of doing that.
[00:12:18]
Something like, you know, a Google Drive kind of style project. There's one question about what this looks like if you're just building in like a backend app, like an API, is it just kind of similar to what the service layer here would look like? Yeah, I mean, honestly, it would be even simpler because essentially the only thing that's not backend focused in this project is when we specifically wrote code in React to render things in the UI or not.
[00:12:42]
So all the service layer, all of the like CASL or attribute-based access control, all those files stay exactly the same. All your actions stay the same, they would essentially be an API route in your case, but the UI is the only place that we actually had to do things specific to the front end. So, 90% of the code we wrote was probably backend focused. The only time we wrote front end code was specifically to like show and hide buttons or prevent access to certain pages.
[00:13:08]
But yeah, I would still recommend the service layer as is, and I would still recommend writing your permissions essentially the exact same way. One follow-up question to the database question, in cases where you're storing permissions in the database, do you recommend sending the permissions over the wire in a JWT or with the JWT token, or client SPA to consume and render the UI? I think it depends a little bit on how your system's already working.
[00:13:39]
I mean, if you're already using, you know, like a JSON web token, then sending those permissions down with it probably makes sense. But if you don't have that type of like communication between the two, maybe it makes the most sense to just send down what your permissions look like in like a JSON file, kind of like how CASL I mentioned can take your permissions and convert them into a format that can then be read on the client.
[00:13:58]
That would be another way of doing it. I find that depending on how you're writing your code, like if you're writing in Next.js, which is pretty much what I used for this project, the nice thing is, is you can just write your code on the server, and you can kind of write what your code will look like on the client, because usually I find that in a lot of applications the permission for someone to do something is decided on the server and it does not change while they're on the current page.
[00:14:21]
So you can just say, hide this button from the server if you're rendering the page on the server, and it'll never show on the button. But if you're using like a true SPA, like you said, like a single page application that calls maybe an API instead of a dedicated backend like Next.js, then that's something where you can just, as part of your API you can send down what those permissions are that they have.
[00:14:40]
It can just be like a single endpoint that says get user permissions or something like that. It'll return down that permission data and then in your front end, you can essentially ingest what that data does. You can have a library that handles that for you or you could send it in like a JSON web token if you want. So I think it really depends if you already have a JSON web token, it makes sense to use it.
[00:14:59]
If you don't, it probably doesn't make sense to implement that just to send down permission logic. Well, if no one's got any questions, that's all I got for you guys. Oh thanks Kyle. Yeah, thanks for sitting through it. I know it was a brain buster at the end.
Learn Straight from the Experts Who Shape the Modern Web
- 250+In-depth Courses
- Industry Leading Experts
- 24Learning Paths
- Live Interactive Workshops