import 'reflect-metadata' import * as express from 'express' import { InversifyExpressUsersController } from './InversifyExpressUsersController' import { results } from 'inversify-express-utils' import { Username } from '@standardnotes/domain-core' import { DeleteAccount } from '../../Domain/UseCase/DeleteAccount/DeleteAccount' import { ChangeCredentials } from '../../Domain/UseCase/ChangeCredentials/ChangeCredentials' import { ClearLoginAttempts } from '../../Domain/UseCase/ClearLoginAttempts' import { GetUserKeyParams } from '../../Domain/UseCase/GetUserKeyParams/GetUserKeyParams' import { GetUserSubscription } from '../../Domain/UseCase/GetUserSubscription/GetUserSubscription' import { IncreaseLoginAttempts } from '../../Domain/UseCase/IncreaseLoginAttempts' import { InviteToSharedSubscription } from '../../Domain/UseCase/InviteToSharedSubscription/InviteToSharedSubscription' import { UpdateUser } from '../../Domain/UseCase/UpdateUser' import { User } from '../../Domain/User/User' describe('InversifyExpressUsersController', () => { let updateUser: UpdateUser let deleteAccount: DeleteAccount let getUserKeyParams: GetUserKeyParams let getUserSubscription: GetUserSubscription let clearLoginAttempts: ClearLoginAttempts let increaseLoginAttempts: IncreaseLoginAttempts let changeCredentials: ChangeCredentials let inviteToSharedSubscription: InviteToSharedSubscription let request: express.Request let response: express.Response let user: User const createController = () => new InversifyExpressUsersController( updateUser, getUserKeyParams, deleteAccount, getUserSubscription, clearLoginAttempts, increaseLoginAttempts, changeCredentials, ) beforeEach(() => { updateUser = {} as jest.Mocked updateUser.execute = jest.fn() deleteAccount = {} as jest.Mocked deleteAccount.execute = jest.fn().mockReturnValue({ success: true, message: 'A OK', responseCode: 200 }) user = {} as jest.Mocked user.uuid = '123' user.email = 'test@test.te' getUserKeyParams = {} as jest.Mocked getUserKeyParams.execute = jest.fn() getUserSubscription = {} as jest.Mocked getUserSubscription.execute = jest.fn() changeCredentials = {} as jest.Mocked changeCredentials.execute = jest.fn() clearLoginAttempts = {} as jest.Mocked clearLoginAttempts.execute = jest.fn() increaseLoginAttempts = {} as jest.Mocked increaseLoginAttempts.execute = jest.fn() inviteToSharedSubscription = {} as jest.Mocked inviteToSharedSubscription.execute = jest.fn() request = { headers: {}, body: {}, params: {}, } as jest.Mocked response = { locals: {}, } as jest.Mocked response.status = jest.fn().mockReturnThis() response.setHeader = jest.fn() response.send = jest.fn() }) it('should update user', async () => { request.body.version = '002' request.body.api = '20190520' request.body.origination = 'test' request.params.userId = '123' request.headers['user-agent'] = 'Google Chrome' response.locals.user = user updateUser.execute = jest.fn().mockReturnValue({ success: true, authResponse: { foo: 'bar' } }) const httpResponse = await createController().update(request, response) const result = await httpResponse.executeAsync() expect(updateUser.execute).toHaveBeenCalledWith({ apiVersion: '20190520', updatedWithUserAgent: 'Google Chrome', user: { uuid: '123', email: 'test@test.te', }, }) expect(await result.content.readAsStringAsync()).toEqual('{"foo":"bar"}') }) it('should not update user if session has read only access', async () => { request.body.version = '002' request.body.api = '20190520' request.body.origination = 'test' request.params.userId = '123' request.headers['user-agent'] = 'Google Chrome' response.locals.user = user response.locals.readOnlyAccess = true const httpResponse = await createController().update(request, response) const result = await httpResponse.executeAsync() expect(updateUser.execute).not.toHaveBeenCalled() expect(result.statusCode).toEqual(401) }) it('should not update a user if the procedure fails', async () => { request.body.version = '002' request.body.api = '20190520' request.body.origination = 'test' request.params.userId = '123' request.headers['user-agent'] = 'Google Chrome' response.locals.user = user updateUser.execute = jest.fn().mockReturnValue({ success: false }) const httpResponse = await createController().update(request, response) const result = await httpResponse.executeAsync() expect(updateUser.execute).toHaveBeenCalledWith({ apiVersion: '20190520', updatedWithUserAgent: 'Google Chrome', user: { uuid: '123', email: 'test@test.te', }, }) expect(result.statusCode).toEqual(400) expect(await result.content.readAsStringAsync()).toEqual('{"error":{"message":"Could not update user."}}') }) it('should not update a user if it is not the same as logged in user', async () => { request.body.version = '002' request.body.api = '20190520' request.body.origination = 'test' request.params.userId = '234' request.headers['user-agent'] = 'Google Chrome' response.locals.user = user const httpResponse = await createController().update(request, response) const result = await httpResponse.executeAsync() expect(updateUser.execute).not.toHaveBeenCalled() expect(result.statusCode).toEqual(401) expect(await result.content.readAsStringAsync()).toEqual('{"error":{"message":"Operation not allowed."}}') }) it('should delete user', async () => { request.params.email = 'test@test.te' const httpResponse = await createController().deleteAccount(request) const result = await httpResponse.executeAsync() expect(deleteAccount.execute).toHaveBeenCalledWith({ email: 'test@test.te' }) expect(result.statusCode).toEqual(200) expect(await result.content.readAsStringAsync()).toEqual('{"message":"A OK"}') }) it('should get user key params', async () => { request.query = { email: 'test@test.te', uuid: '1-2-3', } getUserKeyParams.execute = jest.fn().mockReturnValue({ foo: 'bar' }) const httpResponse = await createController().keyParams(request) const result = await httpResponse.executeAsync() expect(getUserKeyParams.execute).toHaveBeenCalledWith({ email: 'test@test.te', userUuid: '1-2-3', authenticated: false, }) expect(result.statusCode).toEqual(200) }) it('should get authenticated user key params', async () => { request.query = { email: 'test@test.te', uuid: '1-2-3', authenticated: 'true', } getUserKeyParams.execute = jest.fn().mockReturnValue({ foo: 'bar' }) const httpResponse = await createController().keyParams(request) const result = await httpResponse.executeAsync() expect(getUserKeyParams.execute).toHaveBeenCalledWith({ email: 'test@test.te', userUuid: '1-2-3', authenticated: true, }) expect(result.statusCode).toEqual(200) }) it('should not get user key params if email and user uuid is missing', async () => { request.query = {} getUserKeyParams.execute = jest.fn().mockReturnValue({ foo: 'bar' }) const httpResponse = await createController().keyParams(request) const result = await httpResponse.executeAsync() expect(getUserKeyParams.execute).not.toHaveBeenCalled() expect(result.statusCode).toEqual(400) }) it('should get user subscription', async () => { request.params.userUuid = '1-2-3' response.locals.user = { uuid: '1-2-3', } getUserSubscription.execute = jest.fn().mockReturnValue({ success: true, }) const httpResponse = await createController().getSubscription(request, response) const result = await httpResponse.executeAsync() expect(getUserSubscription.execute).toHaveBeenCalledWith({ userUuid: '1-2-3', }) expect(result.statusCode).toEqual(200) }) it('should not get user subscription if the user with provided uuid does not exist', async () => { request.params.userUuid = '1-2-3' response.locals.user = { uuid: '1-2-3', } getUserSubscription.execute = jest.fn().mockReturnValue({ success: false, }) const httpResponse = await createController().getSubscription(request, response) const result = await httpResponse.executeAsync() expect(getUserSubscription.execute).toHaveBeenCalledWith({ userUuid: '1-2-3' }) expect(result.statusCode).toEqual(400) }) it('should not get user subscription if not allowed', async () => { request.params.userUuid = '1-2-3' response.locals.user = { uuid: '2-3-4', } getUserSubscription.execute = jest.fn() const httpResponse = await createController().getSubscription(request, response) const result = await httpResponse.executeAsync() expect(getUserSubscription.execute).not.toHaveBeenCalled() expect(result.statusCode).toEqual(401) }) it('should change a password', async () => { request.body.version = '004' request.body.api = '20190520' request.body.current_password = 'test123' request.body.new_password = 'test234' request.body.pw_nonce = 'asdzxc' request.body.origination = 'change-password' request.body.created = '123' request.headers['user-agent'] = 'Google Chrome' response.locals.user = user changeCredentials.execute = jest.fn().mockReturnValue({ success: true, authResponse: { foo: 'bar' } }) const httpResponse = await createController().changeCredentials(request, response) const result = await httpResponse.executeAsync() expect(changeCredentials.execute).toHaveBeenCalledWith({ apiVersion: '20190520', updatedWithUserAgent: 'Google Chrome', currentPassword: 'test123', newPassword: 'test234', kpCreated: '123', kpOrigination: 'change-password', pwNonce: 'asdzxc', protocolVersion: '004', username: Username.create('test@test.te').getValue(), }) expect(clearLoginAttempts.execute).toHaveBeenCalled() expect(await result.content.readAsStringAsync()).toEqual('{"foo":"bar"}') }) it('should not change a password if session has read only access', async () => { request.body.version = '004' request.body.api = '20190520' request.body.current_password = 'test123' request.body.new_password = 'test234' request.body.pw_nonce = 'asdzxc' request.body.origination = 'change-password' request.body.created = '123' request.headers['user-agent'] = 'Google Chrome' response.locals.user = user response.locals.readOnlyAccess = true const httpResponse = await createController().changeCredentials(request, response) const result = await httpResponse.executeAsync() expect(changeCredentials.execute).not.toHaveBeenCalled() expect(clearLoginAttempts.execute).not.toHaveBeenCalled() expect(result.statusCode).toEqual(401) }) it('should indicate if changing a password fails', async () => { request.body.version = '004' request.body.api = '20190520' request.body.current_password = 'test123' request.body.new_password = 'test234' request.body.pw_nonce = 'asdzxc' request.headers['user-agent'] = 'Google Chrome' response.locals.user = user changeCredentials.execute = jest.fn().mockReturnValue({ success: false, errorMessage: 'Something bad happened' }) const httpResponse = await createController().changeCredentials(request, response) const result = await httpResponse.executeAsync() expect(increaseLoginAttempts.execute).toHaveBeenCalled() expect(result.statusCode).toEqual(401) expect(await result.content.readAsStringAsync()).toEqual('{"error":{"message":"Something bad happened"}}') }) it('should not change a password if current password is missing', async () => { request.body.version = '004' request.body.api = '20190520' request.body.new_password = 'test234' request.body.pw_nonce = 'asdzxc' request.headers['user-agent'] = 'Google Chrome' response.locals.user = user const httpResponse = await createController().changeCredentials(request, response) const result = await httpResponse.executeAsync() expect(changeCredentials.execute).not.toHaveBeenCalled() expect(result.statusCode).toEqual(400) expect(await result.content.readAsStringAsync()).toEqual( '{"error":{"message":"Your current password is required to change your password. Please update your application if you do not see this option."}}', ) }) it('should not change a password if new password is missing', async () => { request.body.version = '004' request.body.api = '20190520' request.body.current_password = 'test123' request.body.pw_nonce = 'asdzxc' request.headers['user-agent'] = 'Google Chrome' response.locals.user = user const httpResponse = await createController().changeCredentials(request, response) const result = await httpResponse.executeAsync() expect(changeCredentials.execute).not.toHaveBeenCalled() expect(result.statusCode).toEqual(400) expect(await result.content.readAsStringAsync()).toEqual( '{"error":{"message":"Your new password is required to change your password. Please try again."}}', ) }) it('should not change a password if password nonce is missing', async () => { request.body.version = '004' request.body.api = '20190520' request.body.current_password = 'test123' request.body.new_password = 'test234' request.headers['user-agent'] = 'Google Chrome' response.locals.user = user const httpResponse = await createController().changeCredentials(request, response) const result = await httpResponse.executeAsync() expect(changeCredentials.execute).not.toHaveBeenCalled() expect(result.statusCode).toEqual(400) expect(await result.content.readAsStringAsync()).toEqual( '{"error":{"message":"The change password request is missing new auth parameters. Please try again."}}', ) }) })