1
0
mirror of https://github.com/sasjs/server.git synced 2025-12-10 11:24:35 +00:00

feat: user operation apis added

This commit is contained in:
Saad Jutt
2021-11-04 05:51:29 +05:00
parent 9f17b17e31
commit 728f277f5c
11 changed files with 190 additions and 26 deletions

View File

@@ -0,0 +1,20 @@
import bcrypt from 'bcryptjs'
import User from '../model/User'
export const deleteUser = async (
username: string,
isAdmin: boolean,
data: any
) => {
const { password } = data
const user = await User.findOne({ username })
if (!user) throw new Error('Username is not found.')
if (!isAdmin) {
const validPass = await bcrypt.compare(password, user.password)
if (!validPass) throw new Error('Invalid password.')
}
await User.deleteOne({ username })
}

View File

@@ -0,0 +1,35 @@
import bcrypt from 'bcryptjs'
import User from '../model/User'
export const updateUser = async (currentUsername: string, data: any) => {
const { displayName, username, password, isAdmin, isActive } = data
const params: any = { displayName, isAdmin, isActive }
if (username && currentUsername !== username) {
// Checking if username is already in the database
const usernameExist = await User.findOne({ username })
if (usernameExist) throw new Error('Username already exists.')
params.username = username
}
if (password) {
// Hash passwords
const salt = await bcrypt.genSalt(10)
params.password = await bcrypt.hash(password, salt)
}
const updatedUser = await User.findOneAndUpdate(
{ username: currentUsername },
params,
{ new: true }
)
return {
displayName: updatedUser.displayName,
username: updatedUser.username,
isAdmin: updatedUser.isAdmin,
isActive: updatedUser.isActive
}
}

View File

@@ -1,5 +1,5 @@
import jwt from 'jsonwebtoken'
import { verifyTokenInDB } from './verifyTokenInDB'
import { verifyTokenInDB } from '../utils'
export const authenticateAccessToken = (req: any, res: any, next: any) => {
authenticateToken(

2
src/middlewares/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from './authenticateToken'
export * from './verifyAdmin'

View File

@@ -0,0 +1,5 @@
export const verifyAdmin = (req: any, res: any, next: any) => {
const { user } = req
if (!user?.isAdmin) return res.status(401).send('Admin account required')
next()
}

View File

@@ -5,9 +5,13 @@ import jwt from 'jsonwebtoken'
import Client from '../../model/Client'
import User from '../../model/User'
import {
authenticateAccessToken,
authenticateRefreshToken,
authenticateRefreshToken
} from '../../middlewares'
import {
authorizeValidation,
removeTokensInDB,
saveTokensInDB,

View File

@@ -1,12 +1,13 @@
import express from 'express'
import dotenv from 'dotenv'
import { authenticateAccessToken, verifyAdmin } from '../../middlewares'
import driveRouter from './drive'
import stpRouter from './stp'
import userRouter from './user'
import clientRouter from './client'
import authRouter, { connectDB } from './auth'
import { authenticateAccessToken } from '../../utils'
dotenv.config()
connectDB()
@@ -15,14 +16,8 @@ const router = express.Router()
router.use('/drive', authenticateAccessToken, driveRouter)
router.use('/stp', authenticateAccessToken, stpRouter)
router.use('/user', authenticateAccessToken, verifyAdmin, userRouter)
router.use('/user', userRouter)
router.use('/client', authenticateAccessToken, verifyAdmin, clientRouter)
router.use('/auth', authRouter)
function verifyAdmin(req: any, res: any, next: any) {
const { user } = req
if (!user?.isAdmin) return res.status(403).send('Admin account required')
next()
}
export default router

View File

@@ -1,25 +1,99 @@
import express from 'express'
import { createUser } from '../../controllers/createUser'
import { registerUserValidation } from '../../utils'
import { updateUser } from '../../controllers/updateUser'
import { deleteUser } from '../../controllers/deleteUser'
import { authenticateAccessToken, verifyAdmin } from '../../middlewares'
import User from '../../model/User'
import {
deleteUserValidation,
registerUserValidation,
updateUserValidation
} from '../../utils'
const userRouter = express.Router()
userRouter.post('/', async (req, res) => {
userRouter.post('/', authenticateAccessToken, verifyAdmin, async (req, res) => {
const { error, value: data } = registerUserValidation(req.body)
if (error) return res.status(400).send(error.details[0].message)
try {
const savedUser = await createUser(data)
res.send({
displayName: savedUser.displayName,
username: savedUser.username,
isAdmin: savedUser.isAdmin,
isActive: savedUser.isActive,
tokens: []
})
res.send(savedUser)
} catch (err: any) {
res.status(403).send(err.toString())
}
})
userRouter.get('/', authenticateAccessToken, async (req, res) => {
try {
const users = await User.find({})
.select({ _id: 0, username: 1, displayName: 1, isAdmin: 1, isActive: 1 })
.exec()
res.send(users)
} catch (err: any) {
res.status(403).send(err.toString())
}
})
userRouter.get('/:username', authenticateAccessToken, async (req: any, res) => {
const { username } = req.params
try {
const user = await User.findOne({ username })
.select({ _id: 0, username: 1, displayName: 1, isAdmin: 1, isActive: 1 })
.exec()
res.send(user)
} catch (err: any) {
res.status(403).send(err.toString())
}
})
userRouter.patch(
'/:username',
authenticateAccessToken,
async (req: any, res) => {
const { user } = req
const { username } = req.params
// only an admin can update other users
if (!user.isAdmin && user.username !== username) {
return res.status(401).send('Admin account required')
}
// only an admin can update `isActive` and `isAdmin` fields
const { error, value: data } = updateUserValidation(req.body, user.isAdmin)
if (error) return res.status(400).send(error.details[0].message)
try {
const user = await updateUser(username, data)
res.send(user)
} catch (err: any) {
res.status(403).send(err.toString())
}
}
)
userRouter.delete(
'/:username',
authenticateAccessToken,
async (req: any, res) => {
const { user } = req
const { username } = req.params
// only an admin can delete other users
if (!user.isAdmin && user.username !== username) {
return res.status(401).send('Admin account required')
}
const { error, value: data } = deleteUserValidation(req.body, user.isAdmin)
if (error) return res.status(400).send(error.details[0].message)
try {
await deleteUser(username, user.isAdmin, data)
res.status(200).send('Account Deleted!')
} catch (err: any) {
res.status(403).send(err.toString())
}
}
)
export default userRouter

View File

@@ -1,4 +1,3 @@
export * from './authenticateToken'
export * from './file'
export * from './removeTokensInDB'
export * from './saveTokensInDB'

View File

@@ -1,12 +1,12 @@
import Joi from 'joi'
const usernameSchema = Joi.string().alphanum().min(6).max(20).required()
const passwordSchema = Joi.string().min(6).max(1024).required()
const usernameSchema = Joi.string().alphanum().min(6).max(20)
const passwordSchema = Joi.string().min(6).max(1024)
export const authorizeValidation = (data: any): Joi.ValidationResult =>
Joi.object({
username: usernameSchema,
password: passwordSchema,
username: usernameSchema.required(),
password: passwordSchema.required(),
clientId: Joi.string().required()
}).validate(data)
@@ -19,12 +19,40 @@ export const tokenValidation = (data: any): Joi.ValidationResult =>
export const registerUserValidation = (data: any): Joi.ValidationResult =>
Joi.object({
displayName: Joi.string().min(6).required(),
username: usernameSchema,
password: passwordSchema,
username: usernameSchema.required(),
password: passwordSchema.required(),
isAdmin: Joi.boolean(),
isActive: Joi.boolean()
}).validate(data)
export const deleteUserValidation = (
data: any,
isAdmin: boolean = false
): Joi.ValidationResult =>
Joi.object(
isAdmin
? {}
: {
password: passwordSchema.required()
}
).validate(data)
export const updateUserValidation = (
data: any,
isAdmin: boolean = false
): Joi.ValidationResult => {
const validationChecks: any = {
displayName: Joi.string().min(6),
username: usernameSchema,
password: passwordSchema
}
if (isAdmin) {
validationChecks.isAdmin = Joi.boolean()
validationChecks.isActive = Joi.boolean()
}
return Joi.object(validationChecks).validate(data)
}
export const registerClientValidation = (data: any): Joi.ValidationResult =>
Joi.object({
clientId: Joi.string().required(),

View File

@@ -8,6 +8,8 @@ export const verifyTokenInDB = async (
) => {
const dbUser = await User.findOne({ username })
if (!dbUser) return undefined
const currentTokenObj = dbUser.tokens.find(
(tokenObj: any) => tokenObj.clientId === clientId
)