0

I am learning by building. I am building a blog CMS with Nodejs, reactjs, and mongodb. I have two roles: users and admin. I would like admin to be able to delete any user. I wrote codes that enabled a user to delete his/her own account. How do I go about making the admin to be able to delete any user by clicking a button next to that user?

Here are my codes so far: code for a user to delete him/her self. Once a user deletes him/her self, everything associated with the user also gets deleted. This is working fine.

//delete logic

 router.delete("/:id", async (req, res) =>{
 if(req.body.userId === req.params.id){//we checked if the user id matched

    try{
        const user = await User.findById(req.params.id)//get the user and assign it to user variable
        try{
            await Post.deleteMany({username: user._id})//deleting user posts once the username matches with the variable user object .username
            await Comment.deleteMany({author: user._id})//delete user's comment by checking the comment author's id.
            await Reply.deleteMany({author: user._id})//deletes user's replies
            await User.findByIdAndDelete(req.params.id)//delete the user
            res.status(200).json("User has been deleted")
        } catch(err){
            res.status(500).json(err) //this handles the error if there is one from the server
        }
    }catch(err){
        res.status(404).json("User not found")
    }

       
  } else{
   res.status(401).json("You can only update your account!")
  }

 });

How I tried to write the code for admin to be able to delete a user:

/delete a user by an admin

 router.delete("/:id", async (req, res) =>{
  if(req.body.userId === req.params.id){
     const user = await User.findOne({username: req.body.username})
   if(user && user.role === "admin"){
    try{
        const regUser = await User.findById(req.params.id)//get the user and assign it to  user variable
        try{
            await Post.deleteMany({username: regUser._id})//deleting user posts once the username matches with the variable user object .username
            await Comment.deleteMany({author: regUser._id})//delete user's comment by checking the comment author's id.
            await Reply.deleteMany({author: regUser._id})//deletes user's replies
            await User.findByIdAndDelete(req.params.id)//delete the user
            res.status(200).json("User has been deleted")
        } catch(err){
            res.status(500).json(err) //this handles the error if there is one from the server
        }
    }catch(err){
        res.status(404).json("User not found")
    }


  }else{
     res.status(401).json("You do not have the permission")
  }

   }

   
       


 });

When I tried this code on postman, it kept on loading and didn't deliver anything.

I know that I am not writing the function properly. Kindly provide me with any help to enable me achieve this. Thank you

1 Answer 1

1

I tried to reverse-engineer your request body of your API. And I think it's the following:

{
    body: {
      userId: string
      userName: string
    }
    params: {
      id: string
    }
}

So, trying to reverse engineer what each value is intended for:

  • the params-id obviously is just the parameter which is contained in the URL. So, that's the id of the user which you're trying to delete.

So, what are the userId and userName in your body ?

The issue of security

Judging from your code, the userName and/or userId refer to the user who's logged in and who's performing the operation. Surely, that can't be secure.

You are aware that every user can hit F12 in his webbrowser and see all in/out going requests. It's really easy to modify them and put in the ID of a different user. So, surely, you need more security than just that.

What you need is a "context" which keeps track of the logged in user. e.g. sometimes the entire user object of the logged in user is added on req.context.me.

I've searched for a tutorial that illustrates this, and found this one. It's not entirely what I meant, but it's similar. They store the userId on the req object. Making it available as req.userId.

Aside from security

Having written all that, what you were trying to do is probably the following.

router.delete("/:id", async(req, res) => {

  const loggedInUser = await User.findById(req.body.userId);
  if (loggedInUser && loggedInUser.role === "admin") {
    try {
      const regUser = await User.findById(req.params.id);
      if (regUser == null) {
        throw new Error("user not found");
      }
      
      await Post.deleteMany({
        username: regUser._id
      }) //deleting user posts once the username matches with the variable user object .username
      await Comment.deleteMany({
        author: regUser._id
      }) //delete user's comment by checking the comment author's id.
      await Reply.deleteMany({
        author: regUser._id
      }) //deletes user's replies
      await User.findByIdAndDelete(req.params.id) //delete the user
      res.status(200).json("User has been deleted")
    } catch (err) {
      res.status(500).json(err) //this handles the error if there is one from the server
    }
  } else {
    res.status(401).json("You do not have the permission")
  }
}

As you can see, you don't need the username.

DELETE does not support a body

Whether a DELETE can have a body or not is actually a point of discussion. Some clients/servers support it and some don't. You can find more about this here:

body is empty when parsing DELETE request with express and body-parser

Again, this means that you really shouldn't pass the logged in user through the body.

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you very much. I sincerely and deeply appreciate the time and effort you invested, educating me on vital issue such as the security. I tried this code you wrote on postman but I keep getting "you do not have the permission". The url id is the id of the user I would like to delete. Then inside the body, I provided the id of the admin that want to delete. Did I do it rightly?
@kinhs assuming that you used the modified code, which I posted above, that means that the userId is not properly sent in the body, or that it does not exist in the database, or that it has a different role. The easiest way to find out what is going on, is to add a line console.log(req.body.userId). And just before the if (loggedInUser && line, you should add a console.log(loggedInUser ? 'role:' + loggedInUser.role ': 'user not found' ).
PS: added a link to a tutorial that shows how to have a req.userId at your disposal, deducted from a JWT token.
Yes, I am using the code that you modified.. console.log(req.body.userId) is returning undefined. And from what you spoke about security, you mean I should avoid using using req.body.username and use req.body.userId instead, right? Because I have one other place where I used req.body.userId. I want to understand so that i can go back to the code to modify it. Thanks
@kinhs ... in that case. You're probably running against the "DELETE does not support body" issue. (I've added a link).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.