diff --git a/package-lock.json b/package-lock.json index de7dc74..19618d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 93bd47f..ca06967 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/controllers/user.ts b/src/controllers/user.ts index a6198c1..4a2f790 100644 --- a/src/controllers/user.ts +++ b/src/controllers/user.ts @@ -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([ { + id: 123, username: 'johnusername', displayName: 'John' }, { + id: 456, username: 'starkusername', displayName: 'Stark' } @@ -52,6 +56,7 @@ export default class UserController { * */ @Example({ + 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 { + 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({ + 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 { - 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 => + await User.find({}) + .select({ _id: 0, id: 1, username: 1, displayName: 1 }) + .exec() const createUser = async (data: any): Promise => { const { displayName, username, password, isAdmin, isActive } = data @@ -123,26 +141,29 @@ const createUser = async (data: any): Promise => { 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 }) } diff --git a/src/middlewares/authenticateToken.ts b/src/middlewares/authenticateToken.ts index ae1c041..b4d9ddf 100644 --- a/src/middlewares/authenticateToken.ts +++ b/src/middlewares/authenticateToken.ts @@ -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 diff --git a/src/middlewares/verifyAdminIfNeeded.ts b/src/middlewares/verifyAdminIfNeeded.ts index c7f2b8b..5f65201 100644 --- a/src/middlewares/verifyAdminIfNeeded.ts +++ b/src/middlewares/verifyAdminIfNeeded.ts @@ -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() diff --git a/src/model/User.ts b/src/model/User.ts index 799f7dd..6a6c964 100644 --- a/src/model/User.ts +++ b/src/model/User.ts @@ -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({ - 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({ + 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) diff --git a/src/routes/api/auth.ts b/src/routes/api/auth.ts index 69debc7..08de2cd 100644 --- a/src/routes/api/auth.ts +++ b/src/routes/api/auth.ts @@ -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) diff --git a/src/routes/api/spec/auth.spec.ts b/src/routes/api/spec/auth.spec.ts index 7fcc21c..d627dc5 100644 --- a/src/routes/api/spec/auth.spec.ts +++ b/src/routes/api/spec/auth.spec.ts @@ -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' diff --git a/src/routes/api/spec/client.spec.ts b/src/routes/api/spec/client.spec.ts index db8482e..64bcc3f 100644 --- a/src/routes/api/spec/client.spec.ts +++ b/src/routes/api/spec/client.spec.ts @@ -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' diff --git a/src/routes/api/spec/drive.spec.ts b/src/routes/api/spec/drive.spec.ts index 6528a64..bbc0c83 100644 --- a/src/routes/api/spec/drive.spec.ts +++ b/src/routes/api/spec/drive.spec.ts @@ -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) diff --git a/src/routes/api/spec/user.spec.ts b/src/routes/api/spec/user.spec.ts index 2f40283..c26aa97 100644 --- a/src/routes/api/spec/user.spec.ts +++ b/src/routes/api/spec/user.spec.ts @@ -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') diff --git a/src/routes/api/user.ts b/src/routes/api/user.ts index 3a66fcf..978c117 100644 --- a/src/routes/api/user.ts +++ b/src/routes/api/user.ts @@ -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()) diff --git a/src/types/InfoJWT.ts b/src/types/InfoJWT.ts index 25f0c53..07b7e27 100644 --- a/src/types/InfoJWT.ts +++ b/src/types/InfoJWT.ts @@ -1,4 +1,4 @@ export interface InfoJWT { clientId: string - username: string + userId: number } diff --git a/src/utils/saveTokensInDB.ts b/src/utils/saveTokensInDB.ts index e0dfba1..c89e39d 100644 --- a/src/utils/saveTokensInDB.ts +++ b/src/utils/saveTokensInDB.ts @@ -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( diff --git a/src/utils/verifyTokenInDB.ts b/src/utils/verifyTokenInDB.ts index b6d8220..3ce6917 100644 --- a/src/utils/verifyTokenInDB.ts +++ b/src/utils/verifyTokenInDB.ts @@ -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 }