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

chore: added incremental field 'id' in user collection

This commit is contained in:
Saad Jutt
2021-11-05 03:07:20 +05:00
parent 882f36d30e
commit 2b7dfeb2ea
15 changed files with 289 additions and 168 deletions

82
package-lock.json generated
View File

@@ -14,6 +14,7 @@
"joi": "^17.4.2",
"jsonwebtoken": "^8.5.1",
"mongoose": "^6.0.12",
"mongoose-sequence": "^5.3.1",
"morgan": "^1.10.0",
"multer": "^1.4.3",
"swagger-ui-express": "^4.1.6",
@@ -24,6 +25,7 @@
"@types/express": "^4.17.12",
"@types/jest": "^26.0.24",
"@types/jsonwebtoken": "^8.5.5",
"@types/mongoose-sequence": "^3.0.6",
"@types/morgan": "^1.9.3",
"@types/multer": "^1.4.7",
"@types/node": "^15.12.2",
@@ -2399,6 +2401,25 @@
"integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==",
"dev": true
},
"node_modules/@types/mongoose": {
"version": "5.11.97",
"resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.11.97.tgz",
"integrity": "sha512-cqwOVYT3qXyLiGw7ueU2kX9noE8DPGRY6z8eUxudhXY8NZ7DMKYAxyZkLSevGfhCX3dO/AoX5/SO9lAzfjon0Q==",
"deprecated": "Mongoose publishes its own types, so you do not need to install this package.",
"dev": true,
"dependencies": {
"mongoose": "*"
}
},
"node_modules/@types/mongoose-sequence": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/mongoose-sequence/-/mongoose-sequence-3.0.6.tgz",
"integrity": "sha512-S6DD4rSlSnUI9BQvR/ACtekpylSIm0pEKayG9NqOlkUo3Q/AZLBmdi0IozSGPQ8JcB2ZSm81nLdZPhTqyOqrQg==",
"dev": true,
"dependencies": {
"@types/mongoose": "^5.10.5"
}
},
"node_modules/@types/morgan": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz",
@@ -2824,6 +2845,14 @@
"node": ">=0.10.0"
}
},
"node_modules/async": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"dependencies": {
"lodash": "^4.17.14"
}
},
"node_modules/async-mutex": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz",
@@ -7684,8 +7713,7 @@
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.capitalize": {
"version": "4.2.1",
@@ -8258,6 +8286,18 @@
"url": "https://opencollective.com/mongoose"
}
},
"node_modules/mongoose-sequence": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/mongoose-sequence/-/mongoose-sequence-5.3.1.tgz",
"integrity": "sha512-kQB1ctCdAQT8YdQzoHV0CpBRsO4RNVy03SOkzM6TQKBbGBs1ZgVS4UlKsuvBPaiPt9q5tKgQZvorGJ1awbHDqA==",
"dependencies": {
"async": "^2.5.0",
"lodash": "^4.17.20"
},
"peerDependencies": {
"mongoose": ">=4"
}
},
"node_modules/mongoose/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -16077,6 +16117,24 @@
"integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==",
"dev": true
},
"@types/mongoose": {
"version": "5.11.97",
"resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.11.97.tgz",
"integrity": "sha512-cqwOVYT3qXyLiGw7ueU2kX9noE8DPGRY6z8eUxudhXY8NZ7DMKYAxyZkLSevGfhCX3dO/AoX5/SO9lAzfjon0Q==",
"dev": true,
"requires": {
"mongoose": "*"
}
},
"@types/mongoose-sequence": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/mongoose-sequence/-/mongoose-sequence-3.0.6.tgz",
"integrity": "sha512-S6DD4rSlSnUI9BQvR/ACtekpylSIm0pEKayG9NqOlkUo3Q/AZLBmdi0IozSGPQ8JcB2ZSm81nLdZPhTqyOqrQg==",
"dev": true,
"requires": {
"@types/mongoose": "^5.10.5"
}
},
"@types/morgan": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz",
@@ -16439,6 +16497,14 @@
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
"dev": true
},
"async": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"requires": {
"lodash": "^4.17.14"
}
},
"async-mutex": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz",
@@ -20154,8 +20220,7 @@
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash.capitalize": {
"version": "4.2.1",
@@ -20595,6 +20660,15 @@
}
}
},
"mongoose-sequence": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/mongoose-sequence/-/mongoose-sequence-5.3.1.tgz",
"integrity": "sha512-kQB1ctCdAQT8YdQzoHV0CpBRsO4RNVy03SOkzM6TQKBbGBs1ZgVS4UlKsuvBPaiPt9q5tKgQZvorGJ1awbHDqA==",
"requires": {
"async": "^2.5.0",
"lodash": "^4.17.20"
}
},
"morgan": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",

View File

@@ -29,6 +29,7 @@
"joi": "^17.4.2",
"jsonwebtoken": "^8.5.1",
"mongoose": "^6.0.12",
"mongoose-sequence": "^5.3.1",
"morgan": "^1.10.0",
"multer": "^1.4.3",
"swagger-ui-express": "^4.1.6",
@@ -39,6 +40,7 @@
"@types/express": "^4.17.12",
"@types/jest": "^26.0.24",
"@types/jsonwebtoken": "^8.5.5",
"@types/mongoose-sequence": "^3.0.6",
"@types/morgan": "^1.9.3",
"@types/multer": "^1.4.7",
"@types/node": "^15.12.2",

View File

@@ -15,11 +15,13 @@ import bcrypt from 'bcryptjs'
import User, { UserPayload } from '../model/User'
interface userResponse {
id: number
username: string
displayName: string
}
interface userDetailsResponse {
id: number
displayName: string
username: string
isActive: boolean
@@ -34,10 +36,12 @@ export default class UserController {
*/
@Example<userResponse[]>([
{
id: 123,
username: 'johnusername',
displayName: 'John'
},
{
id: 456,
username: 'starkusername',
displayName: 'Stark'
}
@@ -52,6 +56,7 @@ export default class UserController {
*
*/
@Example<userDetailsResponse>({
id: 1234,
displayName: 'John Snow',
username: 'johnSnow01',
isAdmin: false,
@@ -64,42 +69,55 @@ export default class UserController {
return createUser(body)
}
/**
* Get user properties - such as group memberships, userName, displayName.
* @param userId The user's identifier
* @example userId 1234
*/
@Get('{userId}')
public async getUser(@Path() userId: number): Promise<userDetailsResponse> {
return getUser(userId)
}
/**
* Update user properties - such as displayName. Can be performed either by admins, or the user in question.
* @param username The user's identifier
* @example username "johnSnow01"
* @param userId The user's identifier
* @example userId "1234"
*/
@Example<userDetailsResponse>({
id: 1234,
displayName: 'John Snow',
username: 'johnSnow01',
isAdmin: false,
isActive: true
})
@Patch('{username}')
@Patch('{userId}')
public async updateUser(
@Path() username: string,
@Path() userId: number,
@Body() body: UserPayload
): Promise<userDetailsResponse> {
return updateUser(username, body)
return updateUser(userId, body)
}
/**
* Delete a user. Can be performed either by admins, or the user in question.
* @param username The user's identifier
* @example username "johnSnow01"
* @param userId The user's identifier
* @example userId 1234
*/
@Delete('{username}')
@Delete('{userId}')
public async deleteUser(
@Path() username: string,
@Path() userId: number,
@Body() body: { password?: string },
@Query() @Hidden() isAdmin: boolean = false
) {
return deleteUser(username, isAdmin, body)
return deleteUser(userId, isAdmin, body)
}
}
const getAllUsers = async () =>
await User.find({}).select({ _id: 0, username: 1, displayName: 1 }).exec()
const getAllUsers = async (): Promise<userResponse[]> =>
await User.find({})
.select({ _id: 0, id: 1, username: 1, displayName: 1 })
.exec()
const createUser = async (data: any): Promise<userDetailsResponse> => {
const { displayName, username, password, isAdmin, isActive } = data
@@ -123,26 +141,29 @@ const createUser = async (data: any): Promise<userDetailsResponse> => {
const savedUser = await user.save()
return {
displayName: savedUser.displayName,
username: savedUser.username,
isAdmin: savedUser.isAdmin,
isActive: savedUser.isActive
}
return savedUser
}
const updateUser = async (currentUsername: string, data: any) => {
const getUser = async (id: number) => {
const user = await User.findOne({ id })
.select({
_id: 0,
id: 1,
username: 1,
displayName: 1,
isAdmin: 1,
isActive: 1
})
.exec()
if (!user) throw new Error('User is not found.')
return user
}
const updateUser = async (id: number, 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
}
const params: any = { displayName, username, isAdmin, isActive }
if (password) {
// Hash passwords
@@ -150,31 +171,31 @@ const updateUser = async (currentUsername: string, data: any) => {
params.password = await bcrypt.hash(password, salt)
}
const updatedUser = await User.findOneAndUpdate(
{ username: currentUsername },
params,
{ new: true }
)
const updatedUser = await User.findOneAndUpdate({ id }, params, { new: true })
.select({
_id: 0,
id: 1,
username: 1,
displayName: 1,
isAdmin: 1,
isActive: 1
})
.exec()
if (!updatedUser) throw new Error('Unable to update user')
return {
displayName: updatedUser.displayName,
username: updatedUser.username,
isAdmin: updatedUser.isAdmin,
isActive: updatedUser.isActive
}
return updatedUser
}
const deleteUser = async (username: string, isAdmin: boolean, data: any) => {
const deleteUser = async (id: number, isAdmin: boolean, data: any) => {
const { password } = data
const user = await User.findOne({ username })
if (!user) throw new Error('Username is not found.')
const user = await User.findOne({ id })
if (!user) throw new Error('User is not found.')
if (!isAdmin) {
const validPass = await bcrypt.compare(password, user.password)
if (!validPass) throw new Error('Invalid password.')
}
await User.deleteOne({ username })
await User.deleteOne({ id })
}

View File

@@ -37,7 +37,7 @@ const authenticateToken = (
// verify this valid token's entry in DB
const user = await verifyTokenInDB(
data?.username,
data?.userId,
data?.clientId,
token,
tokenType

View File

@@ -1,8 +1,8 @@
export const verifyAdminIfNeeded = (req: any, res: any, next: any) => {
const { user } = req
const { username } = req.params
const { userId } = req.params
if (!user.isAdmin && user.username !== username) {
if (!user.isAdmin && user.id !== userId) {
return res.status(401).send('Admin account required')
}
next()

View File

@@ -1,4 +1,6 @@
import { Schema, model } from 'mongoose'
import { number } from 'joi'
import mongoose, { Schema, model } from 'mongoose'
const AutoIncrement = require('mongoose-sequence')(mongoose)
export interface UserPayload {
/**
@@ -27,50 +29,52 @@ export interface UserPayload {
isActive?: boolean
}
interface UserSchema extends UserPayload {
interface User extends UserPayload {
id: number
isAdmin: boolean
isActive: boolean
tokens: [{ [key: string]: string }]
}
export default model(
'User',
new Schema<UserSchema>({
displayName: {
type: String,
required: true
},
username: {
type: String,
required: true
},
password: {
type: String,
required: true
},
isAdmin: {
type: Boolean,
default: false
},
isActive: {
type: Boolean,
default: true
},
tokens: [
{
clientId: {
type: String,
required: true
},
accessToken: {
type: String,
required: true
},
refreshToken: {
type: String,
required: true
}
const UserSchema = new Schema<User>({
displayName: {
type: String,
required: true
},
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
isAdmin: {
type: Boolean,
default: false
},
isActive: {
type: Boolean,
default: true
},
tokens: [
{
clientId: {
type: String,
required: true
},
accessToken: {
type: String,
required: true
},
refreshToken: {
type: String,
required: true
}
]
})
)
}
]
})
UserSchema.plugin(AutoIncrement, { inc_field: 'id' })
export default model('User', UserSchema)

View File

@@ -46,14 +46,14 @@ export const connectDB = () => {
}
}
export const saveCode = (username: string, clientId: string, code: string) => {
if (authCodes[username]) return (authCodes[username][clientId] = code)
export const saveCode = (userId: number, clientId: string, code: string) => {
if (authCodes[userId]) return (authCodes[userId][clientId] = code)
authCodes[username] = { [clientId]: code }
return authCodes[username][clientId]
authCodes[userId] = { [clientId]: code }
return authCodes[userId][clientId]
}
export const deleteCode = (username: string, clientId: string) =>
delete authCodes[username][clientId]
export const deleteCode = (userId: number, clientId: string) =>
delete authCodes[userId][clientId]
authRouter.post('/authorize', async (req, res) => {
const { error, value } = authorizeValidation(req.body)
@@ -76,10 +76,10 @@ authRouter.post('/authorize', async (req, res) => {
// generate authorization code against clientId
const userInfo: InfoJWT = {
clientId,
username
userId: user.id
}
const code = saveCode(username, clientId, generateAuthCode(userInfo))
const code = saveCode(user.id, clientId, generateAuthCode(userInfo))
res.json({ code })
})
@@ -93,10 +93,9 @@ authRouter.post('/token', async (req, res) => {
const userInfo = await verifyAuthCode(clientId, code)
if (!userInfo) return res.sendStatus(403)
if (authCodes[userInfo.username][clientId] !== code)
return res.sendStatus(403)
if (authCodes[userInfo.userId][clientId] !== code) return res.sendStatus(403)
deleteCode(userInfo.username, clientId)
deleteCode(userInfo.userId, clientId)
const accessToken = generateAccessToken(userInfo)
const refreshToken = jwt.sign(
@@ -104,15 +103,15 @@ authRouter.post('/token', async (req, res) => {
process.env.REFRESH_TOKEN_SECRET as string
)
await saveTokensInDB(userInfo.username, clientId, accessToken, refreshToken)
await saveTokensInDB(userInfo.userId, clientId, accessToken, refreshToken)
res.json({ accessToken: accessToken, refreshToken: refreshToken })
})
authRouter.post('/refresh', authenticateRefreshToken, async (req: any, res) => {
const { username, clientId } = req.user
const { userId, clientId } = req.user
const userInfo = {
username,
userId,
clientId
}
@@ -122,7 +121,7 @@ authRouter.post('/refresh', authenticateRefreshToken, async (req: any, res) => {
process.env.REFRESH_TOKEN_SECRET as string
)
await saveTokensInDB(userInfo.username, clientId, accessToken, refreshToken)
await saveTokensInDB(userInfo.userId, clientId, accessToken, refreshToken)
res.json({ accessToken: accessToken, refreshToken: refreshToken })
})
@@ -160,7 +159,7 @@ const verifyAuthCode = async (
const clientInfo: InfoJWT = {
clientId: data?.clientId,
username: data?.username
userId: data?.userId
}
if (clientInfo.clientId === clientId) {
return resolve(clientInfo)

View File

@@ -17,6 +17,7 @@ import { saveTokensInDB, verifyTokenInDB } from '../../../utils'
const clientId = 'someclientID'
const clientSecret = 'someclientSecret'
const user = {
id: 1234,
displayName: 'Test User',
username: 'testUsername',
password: '87654321',
@@ -153,7 +154,7 @@ describe('auth', () => {
describe('token', () => {
const userInfo: InfoJWT = {
clientId,
username: user.username
userId: user.id
}
beforeAll(async () => {
await userController.createUser(user)
@@ -166,7 +167,7 @@ describe('auth', () => {
it('should respond with access and refresh tokens', async () => {
const code = saveCode(
userInfo.username,
userInfo.userId,
userInfo.clientId,
generateAuthCode(userInfo)
)
@@ -197,7 +198,7 @@ describe('auth', () => {
it('should respond with Bad Request if clientId is missing', async () => {
const code = saveCode(
userInfo.username,
userInfo.userId,
userInfo.clientId,
generateAuthCode(userInfo)
)
@@ -227,7 +228,7 @@ describe('auth', () => {
it('should respond with Forbidden if clientId is invalid', async () => {
const code = saveCode(
userInfo.username,
userInfo.userId,
userInfo.clientId,
generateAuthCode(userInfo)
)
@@ -245,14 +246,21 @@ describe('auth', () => {
})
describe('refresh', () => {
const refreshToken = generateRefreshToken({
clientId,
username: user.username
})
let refreshToken: string
let currentUser: any
beforeEach(async () => {
await userController.createUser(user)
await saveTokensInDB(user.username, clientId, 'accessToken', refreshToken)
currentUser = await userController.createUser(user)
refreshToken = generateRefreshToken({
clientId,
userId: currentUser.id
})
await saveTokensInDB(
currentUser.id,
clientId,
'accessToken',
refreshToken
)
})
afterEach(async () => {
@@ -289,14 +297,22 @@ describe('auth', () => {
})
describe('logout', () => {
const accessToken = generateAccessToken({
clientId,
username: user.username
})
let accessToken: string
let currentUser: any
beforeEach(async () => {
await userController.createUser(user)
await saveTokensInDB(user.username, clientId, accessToken, 'refreshToken')
currentUser = await userController.createUser(user)
accessToken = generateAccessToken({
clientId,
userId: currentUser.id
})
await saveTokensInDB(
currentUser.id,
clientId,
accessToken,
'refreshToken'
)
})
afterEach(async () => {
@@ -322,7 +338,7 @@ describe('auth', () => {
expect(
await verifyTokenInDB(
user.username,
currentUser.id,
clientId,
accessToken,
'accessToken'

View File

@@ -40,15 +40,17 @@ describe('client', () => {
})
describe('create', () => {
const adminAccessToken = generateAccessToken({
clientId: client.clientId,
username: adminUser.username
})
let adminAccessToken: string
let dbUser: any
beforeAll(async () => {
await userController.createUser(adminUser)
dbUser = await userController.createUser(adminUser)
adminAccessToken = generateAccessToken({
clientId: client.clientId,
userId: dbUser.id
})
await saveTokensInDB(
adminUser.username,
dbUser.id,
client.clientId,
adminAccessToken,
'refreshToken'
@@ -90,13 +92,13 @@ describe('client', () => {
isAdmin: false,
isActive: true
}
const dbUser = await userController.createUser(user)
const accessToken = generateAccessToken({
clientId: client.clientId,
username: user.username
userId: dbUser.id
})
await userController.createUser(user)
await saveTokensInDB(
user.username,
dbUser.id,
client.clientId,
accessToken,
'refreshToken'

View File

@@ -35,14 +35,16 @@ describe('files', () => {
await mongoServer.stop()
})
describe('deploy', () => {
const accessToken = generateAccessToken({
clientId,
username: user.username
})
let accessToken: string
let dbUser: any
beforeAll(async () => {
await controller.createUser(user)
await saveTokensInDB(user.username, clientId, accessToken, 'refreshToken')
dbUser = await controller.createUser(user)
accessToken = generateAccessToken({
clientId,
userId: dbUser.id
})
await saveTokensInDB(dbUser.id, clientId, accessToken, 'refreshToken')
})
const shouldFailAssertion = async (payload: any) => {
const res = await request(app)

View File

@@ -39,15 +39,16 @@ describe('user', () => {
})
describe('create', () => {
const adminAccessToken = generateAccessToken({
clientId,
username: adminUser.username
})
let adminAccessToken: string
beforeEach(async () => {
await controller.createUser(adminUser)
const dbUser = await controller.createUser(adminUser)
adminAccessToken = generateAccessToken({
clientId,
userId: dbUser.id
})
await saveTokensInDB(
adminUser.username,
dbUser.id,
clientId,
adminAccessToken,
'refreshToken'
@@ -84,12 +85,12 @@ describe('user', () => {
})
it('should respond with Forbideen if access token is not of an admin account', async () => {
const dbUser = await controller.createUser(user)
const accessToken = generateAccessToken({
clientId,
username: user.username
userId: dbUser.id
})
await controller.createUser(user)
await saveTokensInDB(user.username, clientId, accessToken, 'refreshToken')
await saveTokensInDB(dbUser.id, clientId, accessToken, 'refreshToken')
const res = await request(app)
.post('/SASjsApi/user')

View File

@@ -5,7 +5,6 @@ import {
verifyAdmin,
verifyAdminIfNeeded
} from '../../middlewares'
import User from '../../model/User'
import {
deleteUserValidation,
registerUserValidation,
@@ -39,13 +38,13 @@ userRouter.get('/', authenticateAccessToken, async (req, res) => {
})
// get one user
userRouter.get('/:username', authenticateAccessToken, async (req: any, res) => {
const { username } = req.params
userRouter.get('/:userId', authenticateAccessToken, async (req: any, res) => {
const { userId } = req.params
const controller = new UserController()
try {
const user = await User.findOne({ username })
.select({ _id: 0, username: 1, displayName: 1, isAdmin: 1, isActive: 1 })
.exec()
res.send(user)
const response = await controller.getUser(userId)
res.send(response)
} catch (err: any) {
res.status(403).send(err.toString())
}
@@ -53,12 +52,12 @@ userRouter.get('/:username', authenticateAccessToken, async (req: any, res) => {
// update user
userRouter.patch(
'/:username',
'/:userId',
authenticateAccessToken,
verifyAdminIfNeeded,
async (req: any, res) => {
const { user } = req
const { username } = req.params
const { userId } = req.params
// only an admin can update `isActive` and `isAdmin` fields
const { error, value: body } = updateUserValidation(req.body, user.isAdmin)
@@ -66,7 +65,7 @@ userRouter.patch(
const controller = new UserController()
try {
const response = await controller.updateUser(username, body)
const response = await controller.updateUser(userId, body)
res.send(response)
} catch (err: any) {
res.status(403).send(err.toString())
@@ -76,12 +75,12 @@ userRouter.patch(
// delete user
userRouter.delete(
'/:username',
'/:userId',
authenticateAccessToken,
verifyAdminIfNeeded,
async (req: any, res) => {
const { user } = req
const { username } = req.params
const { userId } = req.params
// only an admin can delete user without providing password
const { error, value: data } = deleteUserValidation(req.body, user.isAdmin)
@@ -89,7 +88,7 @@ userRouter.delete(
const controller = new UserController()
try {
await controller.deleteUser(username, data, user.isAdmin)
await controller.deleteUser(userId, data, user.isAdmin)
res.status(200).send('Account Deleted!')
} catch (err: any) {
res.status(403).send(err.toString())

View File

@@ -1,4 +1,4 @@
export interface InfoJWT {
clientId: string
username: string
userId: number
}

View File

@@ -1,12 +1,12 @@
import User from '../model/User'
export const saveTokensInDB = async (
username: string,
userId: number,
clientId: string,
accessToken: string,
refreshToken: string
) => {
const user = await User.findOne({ username })
const user = await User.findOne({ id: userId })
if (!user) return
const currentTokenObj = user.tokens.find(

View File

@@ -1,12 +1,12 @@
import User from '../model/User'
export const verifyTokenInDB = async (
username: string,
userId: number,
clientId: string,
token: string,
tokenType: 'accessToken' | 'refreshToken'
) => {
const dbUser = await User.findOne({ username })
const dbUser = await User.findOne({ id: userId })
if (!dbUser) return undefined
@@ -16,8 +16,9 @@ export const verifyTokenInDB = async (
return currentTokenObj?.[tokenType] === token
? {
userId: dbUser.id,
clientId,
username,
username: dbUser.username,
isAdmin: dbUser.isAdmin,
isActive: dbUser.isActive
}