Implementing Role-Based Permissions
00:00 This is our note ID route. So let's go in there. We're going to start in our loader to determine whether or not we should display these. Right now, Kali's got us displaying these all the time, saying that we can delete is true, but that is not the case. So let's come up here and let's first get the user ID.
00:18 So user ID is await, get user ID from our server utils, request here, request there. There we go. So now we've got the user ID and we can use that to determine whether the user has permission.
00:34 So we'll come down here and we'll say permission is equal to now, remember like unauthenticated users can look at this page as well. So if they're unauthenticated, we don't even need to bother looking at the permission table. So we're going to conditionally do this. If the user ID is defined, then we'll do something.
00:52 Otherwise we'll say the permission is null. So the thing that we're going to do is await, charisma permission find first. And we're going to have this query. First, let's add a select of ID of true. That way we aren't pulling more things from the database than we need. We only need to know whether the permission exists.
01:13 And so pulling the ID is just fine. Now our where clause is going to be a little bit tricky. So we're looking for a permission where the roles include the user. So the permission is connected to a user via the roles. And so we're going to select on the roles to make sure or to find all the permissions
01:33 that the user has through their roles. So we're going to say where the permission has an associated role, where some of those roles have a user or have one of their users. So some of the users have an ID that match the user ID and that's going to be users.
01:53 There we go. Hopefully that piece makes sense. I know it's a lot, but we need permissions where some of their roles have some users who have the ID of our user ID. So that is that piece of the where clause. But we also need to say that the entity is note.
02:13 So that's the thing we're trying to determine their permission for. And the action that we want to determine is delete. That's the thing that we're checking for right now. And the access is going to actually depend on whether the note ID's owner matches the user ID.
02:33 So the access is going to depend on the note.owner ID being equal to the user ID. If those two things are equal, then I'm looking at my own note and I need to know whether I have my permission to delete my own notes. If the user IDs do not match, then I need to have permission to delete any note.
02:53 And that is, yeah, that is that query. And so then we can determine whether we can delete based on whether there's a permission for deleting. And that will get my UI up to date.
03:10 So if I come down here and actually before I save this, let's pull this up in an incognito tab. And we can see that. We can delete, we can see that bar. So that's of course not what we want here. So we'll come down here to our UI now that we're passing that can delete and we can say data.candelete.
03:29 If I save that, Cody can still see it because Cody is an admin, has proper permissions, but other users won't be able to see it anymore. So, so far so good. That's great. So now, even if you can't see it, you also need to verify in the action when the action is performed, because again, that's an endpoint
03:48 that people can hit directly. And so we need to grab the owner ID and we can say, we could either put it right here, ID true or do owner ID. Honestly, I can't remember which one I did in the solution. So it doesn't really matter that much clearly.
04:08 And since admins can delete notes, we don't want to filter by the user ID anymore. And so we'll remove that piece of this because we need to get the note whether or not the currently logged in user is the owner. Okay, so now that we've got the note, we're gonna do a permissions check again. That right there is telling me, huh, maybe we've got an abstraction here.
04:28 Yeah, we'll work on that next. But here we've got our user.id. And actually in this case, we're requiring a user anyway. So there's no conditional logic here. This is just gonna be literally do that query because we definitely are going to have the user ID.
04:46 And we'll say user.id right here and user.id. Everything else is the same as before. And now if we say, if there's no permission in this case, then they definitely shouldn't be able to do this at all. So we're gonna throw JSON and the JSON we're gonna throw has an error,
05:05 a status of error. Actually, you know what? Because we're throwing, we're actually going straight to the catch boundary anyway. And so I don't need to do the status error or whatever, like clearly we're in an error. So all I need to do is I'll specify an error is unauthorized. We can say a message just to give people more context
05:25 if somebody's integrating with this, I don't know. Just give them a little bit more context I think is reasonable. You don't have permission to delete this note. You need delete note, whatever, however you wanna do that. And then we'll add a status. This is the important part of 403. And actually, you know what?
05:44 With that, let's come down here, right here and we'll add a 403 handler. And this, yeah, you don't have permission. You don't have permission, dot. And yeah, that should be enough. Like feel free to expound on that if you so desire. And now we also wanna get rid of this invariant response
06:06 right here as well, because now even if the username doesn't match, it is possible that users or the user can delete the note. So we do the permission check more properly with role-based access control here. Okay, let's try this now. If I delete it, it does successfully go away.
06:25 And yeah, we shouldn't even be able to see it if we don't have permission. But let's actually test that out really quick coming down here to the can delete. And we'll just do this true. Come over here. And yeah, that doesn't exist anymore.
06:44 But if I try to delete this, I'm not even logged in. Oh, I'm gonna go to login. So here, I wanna show you this. And so we're gonna log in as this user, log in as that user, there we go. And then if we go to this user and try to delete this note, then you do not have permission.
07:04 Ta-da! So we're in a good place. And of course, yeah, we don't even wanna show things that the user can't do because it's about user experience. So yeah, this user should not be able to do that. And yeah, we can delete notes now, hooray! So let's quickly review the stuff that we did in here to make this work.
07:22 So first off, we need to get the user ID. We're using get user ID instead of require user ID because any user can look at this page. That's a public read as kind of a default for this page. So we get the user ID,
07:39 and then we check for the user's permission if that user ID exists. So if there's a user that's logged in, then we're gonna check and see whether they have permission. The roles is probably the most complicated part. So we're looking for their permission where that permission has some roles, where some of those roles have some users,
07:58 where some of those users have this ID of the user ID. So that filters down to whether a permission exists. Also, that has this entity of note, an action of delete, and access of own if I'm looking at my own note or any if I'm not.
08:18 And that allows us to determine whether or not we're allowed to delete. And if we are allowed to delete, then we are able to display the bar. And then in our action, we require the user, because you can't do anything if you're not logged in. And then right here,
08:36 we're gonna look for the permission in the same way. And if there's not a permission, then we'll return unauthorized. And we have added a unauthorized status handler for this so that users get at least some sense of some useful information there. And that is that.
08:55 Now, I'll mention a couple of things. You could definitely apply this permission stuff to edit as well. So if the user has update capability, then we wanna show the pencil. If they don't, then let's not show the pencil. And so we're gonna have different permissions
09:14 based on the different actions that the user can perform. In fact, you could expand this to say, instead of find first, you could say find many, and then say where the action is either delete or update. So you could do something like that and then use that to determine whether or not we display the bar as whether they can delete
09:36 or they can edit as an example. So that is one thing. Another thing that we could do is probably, now that all we are using this user for is for the user ID, we could probably say require user ID rather than require user. So a couple of options that you have here to expand if you wanna go a little deeper,
09:55 but that's as deep as we're gonna go. We're just adding permissions for the delete. And then in the next step, we're going to add some utilities because I'm looking at this and I'm thinking, I do not wanna have to do that all over my code base. Yeah, no thanks. So we're gonna make this better. So yeah, go ahead. And if you want to,