Skip to content

Commit

Permalink
feat: addition of current user authentication in the request to creat…
Browse files Browse the repository at this point in the history
…e a new order

Updated the create order method that handles post request to create a user order to authenticate
against the current user before an order can be created, the client will have to pass the
authentication token in the request header which will be authenticated before the order is created

BREAKING CHANGE: For an order to be created the client must pass the authentication token in the
request header, otherwise a 401 Unauthorized error will be generated

feat loopbackio#43
  • Loading branch information
austin047 committed Mar 30, 2019
1 parent 021a309 commit d263287
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 31 deletions.
12 changes: 12 additions & 0 deletions src/controllers/user-order.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
HttpErrors,
} from '@loopback/rest';
import {Order} from '../models';
import {authenticate, UserProfile} from '@loopback/authentication';
import {inject} from '@loopback/core';

/**
* Controller for User's Orders
Expand All @@ -35,10 +37,20 @@ export class UserOrderController {
},
},
})
@authenticate('jwt')
async createOrder(
@param.path.string('userId') userId: string,
@inject('authentication.currentUser') currentUser: UserProfile,
@requestBody() order: Order,
): Promise<Order> {
if (currentUser.id !== userId) {
throw new HttpErrors.BadRequest(
`User id does not match looged in user: ${userId} !== ${
currentUser.id
}`,
);
}

if (userId !== order.userId) {
throw new HttpErrors.BadRequest(
`User id does not match: ${userId} !== ${order.userId}`,
Expand Down
137 changes: 106 additions & 31 deletions test/acceptance/user-order.controller.acceptance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import {OrderRepository, UserRepository} from '../../src/repositories';
import {MongoDataSource} from '../../src/datasources';
import {User, Order} from '../../src/models';
import {setupApplication} from './helper';
import {JWTAuthenticationService} from '../../src/services/JWT.authentication.service';
import {
PasswordHasherBindings,
JWTAuthenticationBindings,
} from '../../src/keys';

describe('UserOrderController acceptance tests', () => {
let app: ShoppingApplication;
Expand All @@ -26,42 +31,113 @@ describe('UserOrderController acceptance tests', () => {
await app.stop();
});

it('creates an order for a user with a given orderId', async () => {
const user = await givenAUser();
const userId = user.id.toString();
const order = givenAOrder({userId: userId, orderId: '1'});
describe('Creating new orders for authenticated users', () => {
let plainPassword: string;
let jwtAuthService: JWTAuthenticationService;

await client
.post(`/users/${userId}/orders`)
.send(order)
.expect(200, order);
});
const user = {
email: '[email protected]',
password: 'p4ssw0rd',
firstname: 'Example',
surname: 'User',
};

it('creates an order for a user without a given orderId', async () => {
const user = await givenAUser();
const userId = user.id.toString();
const order = givenAOrder({userId: userId});
before('create new user for user orders', async () => {
app.bind(PasswordHasherBindings.ROUNDS).to(4);

const res = await client
.post(`/users/${userId}/orders`)
.send(order)
.expect(200);
const passwordHasher = await app.get(
PasswordHasherBindings.PASSWORD_HASHER,
);
plainPassword = user.password;
user.password = await passwordHasher.hashPassword(user.password);
jwtAuthService = await app.get(JWTAuthenticationBindings.SERVICE);
});

expect(res.body.orderId).to.be.a.String();
delete res.body.orderId;
expect(res.body).to.deepEqual(order);
});
it('creates an order for a user with a given orderId', async () => {
const newUser = await userRepo.create(user);
const userId = newUser.id.toString();
const order = givenAOrder({userId: userId, orderId: '1'});

const token = await jwtAuthService.getAccessTokenForUser({
email: newUser.email,
password: plainPassword,
});

await client
.post(`/users/${userId}/orders`)
.send(order)
.set('Authorization', 'Bearer ' + token)
.expect(200, order);
});

it('creates an order for a user without a given orderId', async () => {
const newUser = await userRepo.create(user);
const userId = newUser.id.toString();

const token = await jwtAuthService.getAccessTokenForUser({
email: newUser.email,
password: plainPassword,
});

const order = givenAOrder({userId: userId});

const res = await client
.post(`/users/${userId}/orders`)
.send(order)
.set('Authorization', 'Bearer ' + token)
.expect(200);

it('throws an error when a userId in path does not match body', async () => {
const user = await givenAUser();
const userId = user.id.toString();
const order = givenAOrder({userId: 'hello'});
expect(res.body.orderId).to.be.a.String();
delete res.body.orderId;
expect(res.body).to.deepEqual(order);
});

it('throws an error when a userId in path does not match body', async () => {
const newUser = await userRepo.create(user);
const userId = newUser.id.toString();

const token = await jwtAuthService.getAccessTokenForUser({
email: newUser.email,
password: plainPassword,
});

const order = givenAOrder({userId: 'hello'});

await client
.post(`/users/${userId}/orders`)
.set('Content-Type', 'application/json')
.set('Authorization', 'Bearer ' + token)
.send(order)
.expect(400);
});

it('throws an error when a user is not authenticated', async () => {
const newUser = await userRepo.create(user);
const userId = newUser.id.toString();

const order = givenAOrder({userId: userId});

await client
.post(`/users/${userId}/orders`)
.set('Content-Type', 'application/json')
.send(order)
.expect(400);
await client
.post(`/users/${userId}/orders`)
.send(order)
.expect(401);
});

it('throws an error when a user with wrong token is provided', async () => {
const newUser = await userRepo.create(user);
const userId = newUser.id.toString();

const order = givenAOrder({userId: userId});

await client
.post(`/users/${userId}/orders`)
.send(order)
.set(
'Authorization',
'Bearer ' + 'Wrong token - IjoidGVzdEBsb29wYmFjay5p',
)
.expect(401);
});
});

// TODO(virkt25): Implement after issue below is fixed.
Expand Down Expand Up @@ -128,7 +204,6 @@ describe('UserOrderController acceptance tests', () => {
firstname: 'Example',
surname: 'User',
};

return await userRepo.create(user);
}

Expand Down

0 comments on commit d263287

Please sign in to comment.