diff --git a/api/src/controllers/group.ts b/api/src/controllers/group.ts index 98a9467..5371201 100644 --- a/api/src/controllers/group.ts +++ b/api/src/controllers/group.ts @@ -170,7 +170,7 @@ const getGroup = async (groupId: number): Promise => { 'users', 'id username displayName -_id' )) as unknown as GroupDetailsResponse - if (!group) throw new Error('Group is not found.') + if (!group) throw new Error('Group not found.') return { groupId: group.groupId, @@ -184,40 +184,30 @@ const getGroup = async (groupId: number): Promise => { const addUserToGroup = async ( groupId: number, userId: number -): Promise => { - const group = await Group.findOne({ groupId }) - if (!group) throw new Error('Group not found') - - const user = await User.findOne({ id: userId }) - if (!user) throw new Error('User not found') - - const updatedGroup = (await group.addUser( - user._id - )) as unknown as GroupDetailsResponse - if (!updatedGroup) throw new Error('Unable to update group') - - return { - groupId: updatedGroup.groupId, - name: updatedGroup.name, - description: updatedGroup.description, - isActive: updatedGroup.isActive, - users: updatedGroup.users - } -} +): Promise => + updateUsersListInGroup(groupId, userId, 'addUser') const removeUserFromGroup = async ( groupId: number, userId: number +): Promise => + updateUsersListInGroup(groupId, userId, 'removeUser') + +const updateUsersListInGroup = async ( + groupId: number, + userId: number, + action: 'addUser' | 'removeUser' ): Promise => { const group = await Group.findOne({ groupId }) - if (!group) throw new Error('Group not found') + if (!group) throw new Error('Group not found.') const user = await User.findOne({ id: userId }) - if (!user) throw new Error('User not found') + if (!user) throw new Error('User not found.') + + const updatedGroup = (action === 'addUser' + ? await group.addUser(user._id) + : await group.removeUser(user._id)) as unknown as GroupDetailsResponse - const updatedGroup = (await group.removeUser( - user._id - )) as unknown as GroupDetailsResponse if (!updatedGroup) throw new Error('Unable to update group') return { diff --git a/api/src/routes/api/group.ts b/api/src/routes/api/group.ts index a4d14e5..6ae4fdc 100644 --- a/api/src/routes/api/group.ts +++ b/api/src/routes/api/group.ts @@ -49,6 +49,7 @@ groupRouter.get('/:groupId', authenticateAccessToken, async (req: any, res) => { groupRouter.post( '/:groupId/:userId', authenticateAccessToken, + verifyAdmin, async (req: any, res) => { const { groupId, userId } = req.params @@ -65,6 +66,7 @@ groupRouter.post( groupRouter.delete( '/:groupId/:userId', authenticateAccessToken, + verifyAdmin, async (req: any, res) => { const { groupId, userId } = req.params @@ -81,6 +83,7 @@ groupRouter.delete( groupRouter.delete( '/:groupId', authenticateAccessToken, + verifyAdmin, async (req: any, res) => { const { groupId } = req.params diff --git a/api/src/routes/api/index.ts b/api/src/routes/api/index.ts index d10a781..2bce3d5 100644 --- a/api/src/routes/api/index.ts +++ b/api/src/routes/api/index.ts @@ -16,12 +16,12 @@ connectDB() const router = express.Router() +router.use('/auth', authRouter) +router.use('/client', authenticateAccessToken, verifyAdmin, clientRouter) router.use('/drive', authenticateAccessToken, driveRouter) +router.use('/group', groupRouter) router.use('/stp', authenticateAccessToken, stpRouter) router.use('/user', userRouter) -router.use('/group', groupRouter) -router.use('/client', authenticateAccessToken, verifyAdmin, clientRouter) -router.use('/auth', authRouter) router.use( '/', swaggerUi.serve, diff --git a/api/src/routes/api/spec/client.spec.ts b/api/src/routes/api/spec/client.spec.ts index 742097d..e57a84c 100644 --- a/api/src/routes/api/spec/client.spec.ts +++ b/api/src/routes/api/spec/client.spec.ts @@ -42,10 +42,9 @@ describe('client', () => { describe('create', () => { let adminAccessToken: string - let dbUser: any beforeAll(async () => { - dbUser = await userController.createUser(adminUser) + const dbUser = await userController.createUser(adminUser) adminAccessToken = generateAccessToken({ clientId: client.clientId, userId: dbUser.id diff --git a/api/src/routes/api/spec/group.spec.ts b/api/src/routes/api/spec/group.spec.ts new file mode 100644 index 0000000..c941cb8 --- /dev/null +++ b/api/src/routes/api/spec/group.spec.ts @@ -0,0 +1,485 @@ +import mongoose, { Mongoose } from 'mongoose' +import { MongoMemoryServer } from 'mongodb-memory-server' +import request from 'supertest' +import app from '../../../app' +import UserController from '../../../controllers/user' +import GroupController from '../../../controllers/group' +import { generateAccessToken } from '../../../controllers/auth' +import { saveTokensInDB } from '../../../utils' + +const clientId = 'someclientID' +const adminUser = { + displayName: 'Test Admin', + username: 'testAdminUsername', + password: '12345678', + isAdmin: true, + isActive: true +} +const user = { + displayName: 'Test User', + username: 'testUsername', + password: '87654321', + isAdmin: false, + isActive: true +} + +const group = { + name: 'DCGroup1', + description: 'DC group for testing purposes.' +} + +const userController = new UserController() +const groupController = new GroupController() + +describe('group', () => { + let con: Mongoose + let mongoServer: MongoMemoryServer + let adminAccessToken: string + + beforeAll(async () => { + mongoServer = await MongoMemoryServer.create() + con = await mongoose.connect(mongoServer.getUri()) + + adminAccessToken = await generateSaveTokenAndCreateUser() + }) + + afterAll(async () => { + await con.connection.dropDatabase() + await con.connection.close() + await mongoServer.stop() + }) + + describe('create', () => { + afterEach(async () => { + await deleteAllGroups() + }) + + it('should respond with new group', async () => { + const res = await request(app) + .post('/SASjsApi/group') + .auth(adminAccessToken, { type: 'bearer' }) + .send(group) + .expect(200) + + expect(res.body.groupId).toBeTruthy() + expect(res.body.name).toEqual(group.name) + expect(res.body.description).toEqual(group.description) + expect(res.body.isActive).toEqual(true) + expect(res.body.users).toEqual([]) + }) + + it('should respond with Unauthorized if access token is not present', async () => { + const res = await request(app).post('/SASjsApi/group').send().expect(401) + + expect(res.text).toEqual('Unauthorized') + expect(res.body).toEqual({}) + }) + + it('should respond with Unauthorized if access token is not of an admin account', async () => { + const accessToken = await generateSaveTokenAndCreateUser({ + ...user, + username: 'create' + user.username + }) + + const res = await request(app) + .post('/SASjsApi/group') + .auth(accessToken, { type: 'bearer' }) + .send() + .expect(401) + + expect(res.text).toEqual('Admin account required') + expect(res.body).toEqual({}) + }) + + it('should respond with Bad Request if name is missing', async () => { + const res = await request(app) + .post('/SASjsApi/group') + .auth(adminAccessToken, { type: 'bearer' }) + .send({ + ...group, + name: undefined + }) + .expect(400) + + expect(res.text).toEqual(`"name" is required`) + expect(res.body).toEqual({}) + }) + }) + + describe('delete', () => { + afterEach(async () => { + await deleteAllGroups() + }) + + it('should respond with OK when admin user requests', async () => { + const dbGroup = await groupController.createGroup(group) + + const res = await request(app) + .delete(`/SASjsApi/group/${dbGroup.groupId}`) + .auth(adminAccessToken, { type: 'bearer' }) + .send() + .expect(200) + + expect(res.body).toEqual({}) + }) + + it('should respond with Forbidden if groupId is incorrect', async () => { + const res = await request(app) + .delete(`/SASjsApi/group/1234`) + .auth(adminAccessToken, { type: 'bearer' }) + .send() + .expect(403) + + expect(res.text).toEqual('Error: No Group deleted!') + expect(res.body).toEqual({}) + }) + + it('should respond with Unauthorized when access token is not present', async () => { + const res = await request(app) + .delete('/SASjsApi/group/1234') + .send() + .expect(401) + + expect(res.text).toEqual('Unauthorized') + expect(res.body).toEqual({}) + }) + + it('should respond with Unauthorized when access token is not of an admin account', async () => { + const dbGroup = await groupController.createGroup(group) + const accessToken = await generateSaveTokenAndCreateUser({ + ...user, + username: 'delete' + user.username + }) + + const res = await request(app) + .delete(`/SASjsApi/group/${dbGroup.groupId}`) + .auth(accessToken, { type: 'bearer' }) + .send() + .expect(401) + + expect(res.text).toEqual('Admin account required') + expect(res.body).toEqual({}) + }) + }) + + describe('get', () => { + afterEach(async () => { + await deleteAllGroups() + }) + + it('should respond with group', async () => { + const { groupId } = await groupController.createGroup(group) + + const res = await request(app) + .get(`/SASjsApi/group/${groupId}`) + .auth(adminAccessToken, { type: 'bearer' }) + .send() + .expect(200) + + expect(res.body.groupId).toBeTruthy() + expect(res.body.name).toEqual(group.name) + expect(res.body.description).toEqual(group.description) + expect(res.body.isActive).toEqual(true) + expect(res.body.users).toEqual([]) + }) + + it('should respond with group when access token is not of an admin account', async () => { + const accessToken = await generateSaveTokenAndCreateUser({ + ...user, + username: 'get' + user.username + }) + + const { groupId } = await groupController.createGroup(group) + + const res = await request(app) + .get(`/SASjsApi/group/${groupId}`) + .auth(accessToken, { type: 'bearer' }) + .send() + .expect(200) + + expect(res.body.groupId).toBeTruthy() + expect(res.body.name).toEqual(group.name) + expect(res.body.description).toEqual(group.description) + expect(res.body.isActive).toEqual(true) + expect(res.body.users).toEqual([]) + }) + + it('should respond with Unauthorized if access token is not present', async () => { + const res = await request(app) + .get('/SASjsApi/group/1234') + .send() + .expect(401) + + expect(res.text).toEqual('Unauthorized') + expect(res.body).toEqual({}) + }) + + it('should respond with Forbidden if groupId is incorrect', async () => { + const res = await request(app) + .get('/SASjsApi/group/1234') + .auth(adminAccessToken, { type: 'bearer' }) + .send() + .expect(403) + + expect(res.text).toEqual('Error: Group not found.') + expect(res.body).toEqual({}) + }) + }) + + describe('getAll', () => { + afterEach(async () => { + await deleteAllGroups() + }) + + it('should respond with all groups', async () => { + await groupController.createGroup(group) + + const res = await request(app) + .get('/SASjsApi/group') + .auth(adminAccessToken, { type: 'bearer' }) + .send() + .expect(200) + + expect(res.body).toEqual([ + { + groupId: expect.anything(), + name: 'DCGroup1', + description: 'DC group for testing purposes.' + } + ]) + }) + + it('should respond with all groups when access token is not of an admin account', async () => { + await groupController.createGroup(group) + const accessToken = await generateSaveTokenAndCreateUser({ + ...user, + username: 'getAllrandomUser' + }) + + const res = await request(app) + .get('/SASjsApi/group') + .auth(accessToken, { type: 'bearer' }) + .send(user) + .expect(200) + + expect(res.body).toEqual([ + { + groupId: expect.anything(), + name: 'DCGroup1', + description: 'DC group for testing purposes.' + } + ]) + }) + + it('should respond with Unauthorized if access token is not present', async () => { + const res = await request(app).get('/SASjsApi/group').send().expect(401) + + expect(res.text).toEqual('Unauthorized') + expect(res.body).toEqual({}) + }) + }) + + describe('AddUser', () => { + afterEach(async () => { + await deleteAllGroups() + }) + + it('should respond with group having new user in it', async () => { + const dbGroup = await groupController.createGroup(group) + const dbUser = await userController.createUser(user) + + const res = await request(app) + .post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`) + .auth(adminAccessToken, { type: 'bearer' }) + .send() + .expect(200) + + expect(res.body.groupId).toBeTruthy() + expect(res.body.name).toEqual(group.name) + expect(res.body.description).toEqual(group.description) + expect(res.body.isActive).toEqual(true) + expect(res.body.users).toEqual([ + { + id: expect.anything(), + username: user.username, + displayName: user.displayName + } + ]) + }) + + it('should respond with group without duplicating user', async () => { + const dbGroup = await groupController.createGroup(group) + const dbUser = await userController.createUser({ + ...user, + username: 'addUserRandomUser' + }) + await groupController.addUserToGroup(dbGroup.groupId, dbUser.id) + + const res = await request(app) + .post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`) + .auth(adminAccessToken, { type: 'bearer' }) + .send() + .expect(200) + + expect(res.body.groupId).toBeTruthy() + expect(res.body.name).toEqual(group.name) + expect(res.body.description).toEqual(group.description) + expect(res.body.isActive).toEqual(true) + expect(res.body.users).toEqual([ + { + id: expect.anything(), + username: 'addUserRandomUser', + displayName: user.displayName + } + ]) + }) + + it('should respond with Unauthorized if access token is not present', async () => { + const res = await request(app) + .post('/SASjsApi/group/123/123') + .send() + .expect(401) + + expect(res.text).toEqual('Unauthorized') + expect(res.body).toEqual({}) + }) + + it('should respond with Unauthorized if access token is not of an admin account', async () => { + const accessToken = await generateSaveTokenAndCreateUser({ + ...user, + username: 'addUser' + user.username + }) + + const res = await request(app) + .post('/SASjsApi/group/123/123') + .auth(accessToken, { type: 'bearer' }) + .send() + .expect(401) + + expect(res.text).toEqual('Admin account required') + expect(res.body).toEqual({}) + }) + + it('should respond with Forbidden if groupId is incorrect', async () => { + const res = await request(app) + .post('/SASjsApi/group/123/123') + .auth(adminAccessToken, { type: 'bearer' }) + .send() + .expect(403) + + expect(res.text).toEqual('Error: Group not found.') + expect(res.body).toEqual({}) + }) + + it('should respond with Forbidden if userId is incorrect', async () => { + const dbGroup = await groupController.createGroup(group) + const res = await request(app) + .post(`/SASjsApi/group/${dbGroup.groupId}/123`) + .auth(adminAccessToken, { type: 'bearer' }) + .send() + .expect(403) + + expect(res.text).toEqual('Error: User not found.') + expect(res.body).toEqual({}) + }) + }) + + describe('RemoveUser', () => { + afterEach(async () => { + await deleteAllGroups() + }) + + it('should respond with group having user removed from it', async () => { + const dbGroup = await groupController.createGroup(group) + const dbUser = await userController.createUser({ + ...user, + username: 'removeUserRandomUser' + }) + await groupController.addUserToGroup(dbGroup.groupId, dbUser.id) + + const res = await request(app) + .delete(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`) + .auth(adminAccessToken, { type: 'bearer' }) + .send() + .expect(200) + + expect(res.body.groupId).toBeTruthy() + expect(res.body.name).toEqual(group.name) + expect(res.body.description).toEqual(group.description) + expect(res.body.isActive).toEqual(true) + expect(res.body.users).toEqual([]) + }) + + it('should respond with Unauthorized if access token is not present', async () => { + const res = await request(app) + .delete('/SASjsApi/group/123/123') + .send() + .expect(401) + + expect(res.text).toEqual('Unauthorized') + expect(res.body).toEqual({}) + }) + + it('should respond with Unauthorized if access token is not of an admin account', async () => { + const accessToken = await generateSaveTokenAndCreateUser({ + ...user, + username: 'removeUser' + user.username + }) + + const res = await request(app) + .delete('/SASjsApi/group/123/123') + .auth(accessToken, { type: 'bearer' }) + .send() + .expect(401) + + expect(res.text).toEqual('Admin account required') + expect(res.body).toEqual({}) + }) + + it('should respond with Forbidden if groupId is incorrect', async () => { + const res = await request(app) + .delete('/SASjsApi/group/123/123') + .auth(adminAccessToken, { type: 'bearer' }) + .send() + .expect(403) + + expect(res.text).toEqual('Error: Group not found.') + expect(res.body).toEqual({}) + }) + + it('should respond with Forbidden if userId is incorrect', async () => { + const dbGroup = await groupController.createGroup(group) + const res = await request(app) + .delete(`/SASjsApi/group/${dbGroup.groupId}/123`) + .auth(adminAccessToken, { type: 'bearer' }) + .send() + .expect(403) + + expect(res.text).toEqual('Error: User not found.') + expect(res.body).toEqual({}) + }) + }) +}) + +const generateSaveTokenAndCreateUser = async ( + someUser?: any +): Promise => { + const dbUser = await userController.createUser(someUser ?? adminUser) + + return generateAndSaveToken(dbUser.id) +} + +const generateAndSaveToken = async (userId: number) => { + const adminAccessToken = generateAccessToken({ + clientId, + userId + }) + await saveTokensInDB(userId, clientId, adminAccessToken, 'refreshToken') + return adminAccessToken +} + +const deleteAllGroups = async () => { + const { collections } = mongoose.connection + const collection = collections['groups'] + await collection.deleteMany({}) +} diff --git a/api/src/routes/api/spec/user.spec.ts b/api/src/routes/api/spec/user.spec.ts index 1c1a1a0..c15b7ec 100644 --- a/api/src/routes/api/spec/user.spec.ts +++ b/api/src/routes/api/spec/user.spec.ts @@ -364,7 +364,7 @@ describe('user', () => { const res = await request(app) .get(`/SASjsApi/user/${userId}`) .auth(adminAccessToken, { type: 'bearer' }) - .send(user) + .send() .expect(200) expect(res.body.username).toEqual(user.username) @@ -385,7 +385,7 @@ describe('user', () => { const res = await request(app) .get(`/SASjsApi/user/${userId}`) .auth(accessToken, { type: 'bearer' }) - .send(user) + .send() .expect(200) expect(res.body.username).toEqual(user.username) @@ -397,7 +397,7 @@ describe('user', () => { it('should respond with Unauthorized if access token is not present', async () => { const res = await request(app) .get('/SASjsApi/user/1234') - .send(user) + .send() .expect(401) expect(res.text).toEqual('Unauthorized') @@ -410,7 +410,7 @@ describe('user', () => { const res = await request(app) .get('/SASjsApi/user/1234') .auth(adminAccessToken, { type: 'bearer' }) - .send(user) + .send() .expect(403) expect(res.text).toEqual('Error: User is not found.') @@ -435,7 +435,7 @@ describe('user', () => { const res = await request(app) .get('/SASjsApi/user') .auth(adminAccessToken, { type: 'bearer' }) - .send(user) + .send() .expect(200) expect(res.body).toEqual([ @@ -461,7 +461,7 @@ describe('user', () => { const res = await request(app) .get('/SASjsApi/user') .auth(accessToken, { type: 'bearer' }) - .send(user) + .send() .expect(200) expect(res.body).toEqual([ @@ -479,10 +479,7 @@ describe('user', () => { }) it('should respond with Unauthorized if access token is not present', async () => { - const res = await request(app) - .get('/SASjsApi/user') - .send(user) - .expect(401) + const res = await request(app).get('/SASjsApi/user').send().expect(401) expect(res.text).toEqual('Unauthorized') expect(res.body).toEqual({})