تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Prevent Self-Deletion0:00

We have spent a lot of time talking about checking if a User has the ability or the authority to perform a certain task, but we haven't really talked about managing those permissions, which is kind of important as well. Now, of course, all of our permissions right now are based upon roles. Even though we have the article policy, those policies are based upon roles. And managing roles is pretty straightforward. The first thing that we need to do is be sure that a User cannot delete themselves. So we're going to do this in two different places. First is, of course, inside of the index view, we will hide the delete button. But then we also need to be sure that a User cannot delete or destroy themselves. So we will need to add some logic here inside of the destroy method.

But then we also need to be sure that a User cannot delete or destroy themselves. So we will need to add some logic here inside of the destroy method. In fact, since we're here, let's go ahead and do that. We'll check if the currently logged in user is the same as the user that we are trying to destroy. In which case, we're just going to abort with a 403. I don't necessarily want to do a 404 like we have done so far because, well, we know that the user actually exists. So in this particular case, we are essentially saying you are not authorized to delete yourself. And I think that that's going to work just fine. But then we need to turn to the index view. And we want to hide the delete button, which is going to be easy enough.

But then we need to turn to the index view. And we want to hide the delete button, which is going to be easy enough. We will essentially do the same thing. We will check if the currently logged in user's ID is the same as the user that we are currently working with inside of this loop. And actually, this needs to be reversed. If the user is not the same, then we want to display that delete button. In which case, we'll have an end if. And if we check the browser, then yes, the delete button is gone. So not only is that not available for the user, but if they tried to delete themselves by sending a POST request, then they can't do that either. So the next thing that we need to do is handle the editing of a user.

Editing Users and Roles1:56

So not only is that not available for the User, but if they tried to delete themselves by sending a POST request, then they can't do that either. So the next thing that we need to do is handle the editing of a User. And in the browser, we have the name and the email. I think we can get rid of the email just because that really shouldn't be up to us in order to change. So we need to do a couple of things. First of all, let's open up the UserUpdateRequest and let's get rid of the email validation rules. Because if we aren't going to have the email, there's no sense in having the rules there. And then we will get rid of the email field. But the next thing I want to do is display a select box. One for our roles.

But the next thing I want to do is display a select box. One for our roles. But this is going to be a multiple select box so that we can select multiple roles. Let's give this an ID of roles as well. But of course, in order to display a select box for our roles, we need to have the roles so that we can iterate over them. And then we can automatically select the roles that this User is already a member of. So we need to go back to our UsersController. And for the edit method, we need to provide the User to the view, but we also need to provide the roles. In which case, we will just return all of the roles. And yeah, that should get us everything that we need.

In which case, we will just return all of the roles. And yeah, that should get us everything that we need. So back inside of the edit view, we can, first of all, wrap all of this inside of a div. Let's have a label for our roles. I'm just going to copy a label from the name. We'll change these to be roles and roles. But then inside of our select box, we need to iterate over each of those roles. So we'll have roles as role so that we can output the individual options. And the value is going to be the ID of the role. But then we also need to automatically select the role if the user is in that given role.

And the value is going to be the ID of the role. But then we also need to automatically select the role if the user is in that given role. And we already have an easy way of doing that. We have hasRole. We will pass in the role name. And if the user is in that role, then we will output selected. Otherwise, we will output an empty string. And then for the name, simply roleName. And that should get us our select box. I do want to add some styling for the select box,

And that should get us our select box. I do want to add some styling for the select box, just so that it looks similar to our other form fields. But that should be it, at least as far as how we display this inside of the browser. So let's take a look. And inside of editing, okay, we need to change the title here. But in order to update the roles for the author, we can see that admin is selected. And that's not what we would expect. Let's take a look at the editor. Admin is selected.

Fix Role Selection Bug4:48

Let's take a look at the editor. Admin is selected. Author editor is probably going to be admin. Okay, at least it works for admin. Yeah, that's great. Okay, so why is this not working? Did I mess up here? Okay, so we are checking if the user has the given role, then it's selected. But we're getting admin, it's because we are first checking the context. So because we are signed in as admin, we are passing the role name.

But we're getting admin, it's because we are first checking the context. So because we are signed in as admin, we are passing the role name. And our roles are loaded into the context, and we check the context first. That's not going to work in this case. So we found a bug. And the easiest and most straightforward thing that we could do is simply check if the signed-in user is the same as this user that we are currently working with. So if the signed-in user is the same as this user, and we have the context for our roles, then we will check the context. Otherwise, we hit the database.

then we will check the context. Otherwise, we hit the database. We'll need to do the same thing for hasAnyRole. But, okay, that's going to work. But this is writing software. You know, we have a lot of ideas, we implement those ideas, and then later on we find, oh, yeah, that was a good idea, but we didn't plan for, you know, this particular scenario. But, you know, part of developing applications is running into those scenarios as you are developing your application.

But, you know, part of developing applications is running into those scenarios as you are developing your application. So, yeah, we won't dwell on it because it's fixed now. Because whenever we go back to the browser, this is for the author. If we refresh, we can see that author is now selected. And if we take a look at any of the other users like author editor, then we will see author and editor are automatically selected. So now we just need to be able to save the roles whenever we submit that form. But first, I'm going to change the title. This is editUser, not editArticle.

Sync Roles on Update7:00

But first, I'm going to change the title. This is EditUser, not EditArticle. Okay, so I think we are done with our view here. So we can close the edit view. Everything else is going to be inside of our controller and the UserUpdateRequest. Because the first thing we need to do is actually save the roles, which we could leave this as is, update everything that was submitted with the request, because then we also need to sync the roles with what was provided in the request. So that's going to solve that particular issue. The only other thing is performing the validation.

Validate Roles Input7:30

So that's going to solve that particular issue. The only other thing is performing the validation. We want to be sure that if we have roles, because we could get an instance where a User is being taken away from all of the roles. In which case, yes, this could be nullable, but it could also be an array. But we also need to validate the individual elements inside of the roles array. And I wouldn't necessarily say required, but we do need it to be an integer, and it also needs to be an existing role. So that's going to work just fine. However, let's take a look at admin.

Block Removing Admin Role8:04

So that's going to work just fine. However, let's take a look at admin. So our admin User is an admin. What happens if we decide, well, we don't want him to be an admin anymore, and we save that. Well, then we run into a problem. If an admin is attempting to remove the admin role from themselves, that could essentially break the system. We could end up with a system without an admin. And I guess we could put some checks in place to make sure that we always have an admin, but I guess the safer thing to do would be to just be sure that users

And I guess we could put some checks in place to make sure that we always have an admin, but I guess the safer thing to do would be to just be sure that users cannot take away the admin role from themselves. And there are different ways that we can implement that, but I think probably the best place would be here inside of our UserUpdateRequest. Because after all of the default validation, we could perform our own custom validation. And to do that, we use the withValidator method inside of our UserUpdateRequest. And we will call validator after, and this is going to execute after all of the default validation is performed. So if this user has the same ID as the user that we are attempting to modify with the request,

and this is going to execute after all of the default validation is performed. So if this User has the same ID as the User that we are attempting to modify with the request, then we need to check if they are trying to remove the admin role. So we need to get the admin role ID, and we can do that from models\Role, where the name is admin, we get the first ID. And then we're going to check if that admin role is not in the roles from the request. Because if it's absent, then that means the User attempted to remove the admin role. So the roles are from input['roles'], but then we will also have an empty array because it could be possible that roles is not there because it can be null. So we will have a default of an empty array.

because it could be possible that roles is not there because it can be null. So we will have a default of an empty array. So if the User is trying to remove the admin role, then we will simply add a roles error that says you cannot remove the admin role from yourself. And yeah, that should work. Although this is in the wrong spot, we need to put this inside of the input. Okay, so let's go over this because it can be a little confusing. So we are needing to perform our own validation. So we have the withValidator method. After all of the default validation occurs, we will perform our own custom validation here.

So we have the withValidator method. After all of the default validation occurs, we will perform our own custom validation here to where we will check if the currently logged in User is the same as the User we are trying to edit. And we attempt to see if the admin role ID is not in the roles that were submitted with the request. Now, we don't need to check if the User is in the admin role here because in order to perform this particular operation, the User has to be an admin because only admins can admin users. Now, I guess we could add a check here if the User is in the admin role and if they are trying to remove the admin from themselves, but I don't think that that's necessary.

and if they are trying to remove the admin from themselves, but I don't think that that's necessary because we wouldn't be here anyway if the User wasn't an admin. So we are getting the admin role ID because we need to be sure that the admin role ID is not in the roles that were submitted with the request. And if that's the case, then we say that you cannot remove the admin role from yourself. I think we are ready to go. So the first thing I want to do then is let's not play around with our admin User. Let's go to the Author and let's say that the Author is going to be an admin and an author. We'll save that.

Let's go to the Author and let's say that the Author is going to be an Admin and an Author. We'll save that. We didn't get any errors, but that doesn't mean anything. We can see, okay, the Author is now an Admin. Okay, so now that we have an Admin, we can attempt to see if this is going to work. So we will try to take us out of the Admin role. We will try to save it and we didn't see an error. And of course we didn't because we didn't output the errors for the roles, did we? So let's just copy where we have error for the name, we'll say roles, and then we will attempt to do that again.

So let's just copy where we have error for the name, we'll say roles, and then we will attempt to do that again. If we save this, then now we can see our error. You cannot remove the admin role from yourself. Okay, so that is great. That is what we wanted. I want to take the author out of the admin role. So we will set them back to author. We can save it. Let's check it.

We can save it. Let's check it. And sure enough, that is working as intended. Now, as the old saying goes, there's more than one way to implement permission management. This particular approach is probably the most straightforward and the most simple way of doing so. And I like simple. Role-based access and permissions is a very simple concept. And while we could have taken a couple of different approaches, this way works just fine. However, our role-based system is going to fall apart if the role names change. But again, there are solutions for that, which we will look at in the next episode.

However, our role-based system is going to fall apart if the role names change. But again, there are solutions for that, which we will look at in the next episode.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟