300 lines
8.3 KiB
JavaScript
300 lines
8.3 KiB
JavaScript
// Copyright IBM Corp. 2018. All Rights Reserved.
|
|
// Node module: loopback
|
|
// This file is licensed under the MIT License.
|
|
// License text available at https://opensource.org/licenses/MIT
|
|
|
|
'use strict';
|
|
|
|
const debug = require('debug')('test');
|
|
const loopback = require('../');
|
|
const waitForEvent = require('./helpers/wait-for-event');
|
|
const supertest = require('supertest');
|
|
const loggers = require('./helpers/error-loggers');
|
|
const logServerErrorsOtherThan = loggers.logServerErrorsOtherThan;
|
|
const logAllServerErrors = loggers.logAllServerErrors;
|
|
|
|
describe('One user model accessing another user model', () => {
|
|
let app,
|
|
Seller, Customer, Building, CustomAccessToken,
|
|
seniorSeller, juniorSeller, customer,
|
|
seniorToken, juniorToken, customerToken,
|
|
building;
|
|
|
|
/* Setup the following models and relations:
|
|
* - Seller extends User
|
|
* - Customer extends User
|
|
* - Building
|
|
* - CustomAccessToken belongs to Seller or Customer
|
|
* - Building belongs to Seller
|
|
* - Customer belongs to Seller
|
|
*/
|
|
beforeEach(setupAppAndModels);
|
|
|
|
/* Setup the following model instances:
|
|
* - seniorSeller + access token
|
|
* - juniorSeller + access token
|
|
* - customer owned by senior seller + access token
|
|
* - building owned by senior seller
|
|
*/
|
|
beforeEach(setupModelInstances);
|
|
|
|
context('seniorSeller', () => {
|
|
it('can patch seniorSeller', () => {
|
|
logAllServerErrors(app);
|
|
|
|
return supertest(app).patch(`/Sellers/${seniorSeller.id}`)
|
|
.set('Authorization', seniorToken.id)
|
|
.send({name: 'updated name'})
|
|
.expect(200);
|
|
});
|
|
|
|
it('cannot patch juniorSeller', () => {
|
|
logServerErrorsOtherThan(401, app);
|
|
|
|
return supertest(app).patch(`/Sellers/${juniorSeller.id}`)
|
|
.set('Authorization', seniorToken.id)
|
|
.send({name: 'updated name'})
|
|
.expect(401);
|
|
});
|
|
|
|
it('can patch customer', () => {
|
|
logAllServerErrors(app);
|
|
|
|
return supertest(app).patch(`/Customers/${customer.id}`)
|
|
.set('Authorization', seniorToken.id)
|
|
.send({name: 'updated name'})
|
|
.expect(200);
|
|
});
|
|
|
|
it('can retrieve buildings owned by seniorSeller', () => {
|
|
logAllServerErrors(app);
|
|
|
|
return supertest(app).get(`/buildings/${building.id}`)
|
|
.set('Authorization', seniorToken.id)
|
|
.expect(200);
|
|
});
|
|
});
|
|
|
|
context('juniorSeller', () => {
|
|
it('cannot patch seniorSeller', () => {
|
|
logServerErrorsOtherThan(401, app);
|
|
|
|
return supertest(app).patch(`/Sellers/${seniorToken.id}`)
|
|
.set('Authorization', juniorToken.id)
|
|
.send({name: 'updated name'})
|
|
.expect(401);
|
|
});
|
|
|
|
it('can patch juniorSeller', () => {
|
|
logAllServerErrors(app);
|
|
|
|
return supertest(app).patch(`/Sellers/${juniorSeller.id}`)
|
|
.set('Authorization', juniorToken.id)
|
|
.send({name: 'updated name'})
|
|
.expect(200);
|
|
});
|
|
|
|
it('cannot patch customer', () => {
|
|
logServerErrorsOtherThan(401, app);
|
|
|
|
return supertest(app).patch(`/Customers/${customer.id}`)
|
|
.set('Authorization', juniorToken.id)
|
|
.send({name: 'updated name'})
|
|
.expect(401);
|
|
});
|
|
|
|
it('cannot retrieve buildings owned by seniorSeller', () => {
|
|
logServerErrorsOtherThan(401, app);
|
|
|
|
return supertest(app).get(`/buildings/${building.id}`)
|
|
.set('Authorization', juniorToken.id)
|
|
.expect(401);
|
|
});
|
|
});
|
|
|
|
context('customer', () => {
|
|
it('cannot patch seniorSeller', () => {
|
|
logServerErrorsOtherThan(401, app);
|
|
|
|
return supertest(app).patch(`/Sellers/${seniorSeller.id}`)
|
|
.set('Authorization', customerToken.id)
|
|
.send({name: 'updated name'})
|
|
.expect(401);
|
|
});
|
|
|
|
it('cannnot patch juniorSeller', () => {
|
|
logServerErrorsOtherThan(401, app);
|
|
|
|
return supertest(app).patch(`/Sellers/${juniorSeller.id}`)
|
|
.set('Authorization', customerToken.id)
|
|
.send({name: 'updated name'})
|
|
.expect(401);
|
|
});
|
|
|
|
it('can patch customer (own data)', () => {
|
|
logAllServerErrors(app);
|
|
|
|
return supertest(app).patch(`/Customers/${customer.id}`)
|
|
.set('Authorization', customerToken.id)
|
|
.send({name: 'updated name'})
|
|
.expect(200);
|
|
});
|
|
|
|
it('cannot retrieve buildings owned by seniorSeller', () => {
|
|
logServerErrorsOtherThan(401, app);
|
|
|
|
return supertest(app).get(`/buildings/${building.id}`)
|
|
.set('Authorization', customerToken.id)
|
|
.expect(401);
|
|
});
|
|
});
|
|
|
|
function setupAppAndModels() {
|
|
app = loopback({localRegistry: true, loadBuiltinModels: true});
|
|
app.set('_verifyAuthModelRelations', false);
|
|
app.set('remoting', {rest: {handleErrors: false}});
|
|
app.dataSource('db', {connector: 'memory'});
|
|
|
|
Seller = app.registry.createModel({
|
|
name: 'Seller',
|
|
base: 'User',
|
|
saltWorkFactor: 4,
|
|
relations: {
|
|
accessTokens: {
|
|
type: 'hasMany',
|
|
model: 'CustomAccessToken',
|
|
polymorphic: {
|
|
foreignKey: 'userId',
|
|
discriminator: 'principalType',
|
|
},
|
|
},
|
|
buildings: {
|
|
type: 'hasMany',
|
|
model: 'Building',
|
|
},
|
|
customers: {
|
|
type: 'hasMany',
|
|
model: 'Customer',
|
|
},
|
|
},
|
|
});
|
|
|
|
Customer = app.registry.createModel({
|
|
name: 'Customer',
|
|
base: 'User',
|
|
saltWorkFactor: 4,
|
|
relations: {
|
|
accessTokens: {
|
|
type: 'hasMany',
|
|
model: 'CustomAccessToken',
|
|
polymorphic: {
|
|
foreignKey: 'userId',
|
|
discriminator: 'principalType',
|
|
},
|
|
},
|
|
seller: {
|
|
type: 'belongsTo',
|
|
model: 'Seller',
|
|
},
|
|
},
|
|
});
|
|
|
|
Building = app.registry.createModel({
|
|
name: 'Building',
|
|
properties: {
|
|
name: {type: 'string', required: true},
|
|
},
|
|
relations: {
|
|
seller: {
|
|
type: 'belongsTo',
|
|
model: 'Seller',
|
|
},
|
|
},
|
|
acls: [
|
|
{
|
|
accessType: '*',
|
|
principalType: 'ROLE',
|
|
principalId: '$everyone',
|
|
permission: 'DENY',
|
|
},
|
|
{
|
|
accessType: '*',
|
|
principalType: 'ROLE',
|
|
principalId: '$owner',
|
|
permission: 'ALLOW',
|
|
},
|
|
],
|
|
});
|
|
|
|
CustomAccessToken = app.registry.createModel({
|
|
name: 'CustomAccessToken',
|
|
base: 'AccessToken',
|
|
relations: {
|
|
user: {
|
|
type: 'belongsTo',
|
|
idName: 'id',
|
|
polymorphic: {
|
|
idType: 'string',
|
|
foreignKey: 'userId',
|
|
discriminator: 'principalType',
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
app.model(CustomAccessToken, {dataSource: 'db', public: false});
|
|
app.model(Seller, {dataSource: 'db'});
|
|
app.model(Customer, {dataSource: 'db'});
|
|
app.model(Building, {dataSource: 'db'});
|
|
|
|
const builtinModels = app.registry.modelBuilder.models;
|
|
app.model(builtinModels.ACL, {dataSource: 'db', public: false});
|
|
app.model(builtinModels.Role, {dataSource: 'db', public: false});
|
|
app.model(builtinModels.RoleMapping, {dataSource: 'db', public: false});
|
|
|
|
app.enableAuth();
|
|
|
|
app.use(loopback.rest());
|
|
}
|
|
|
|
function setupModelInstances() {
|
|
return Promise.all([
|
|
Seller.create({email: 'seniorSeller@example.com', password: 'pass'}),
|
|
Seller.create({email: 'juniorSeller@example.com', password: 'pass'}),
|
|
]).then((sellers) => {
|
|
seniorSeller = sellers[0];
|
|
juniorSeller = sellers[1];
|
|
|
|
debug('seniorSeller', seniorSeller.toObject());
|
|
debug('juniorSeller', juniorSeller.toObject());
|
|
|
|
return seniorSeller.customers.create({
|
|
email: 'customer@example.com',
|
|
password: 'pass',
|
|
});
|
|
}).then(c => {
|
|
customer = c;
|
|
debug('Customer', customer.toObject());
|
|
|
|
return Promise.all([
|
|
seniorSeller.createAccessToken({}),
|
|
juniorSeller.createAccessToken({}),
|
|
customer.createAccessToken({}),
|
|
]);
|
|
}).then(tokens => {
|
|
seniorToken = tokens[0];
|
|
juniorToken = tokens[1];
|
|
customerToken = tokens[2];
|
|
|
|
debug('seniorSeller token', seniorToken.toObject());
|
|
debug('juniorSeller token', juniorToken.toObject());
|
|
debug('customer token', customerToken.toObject());
|
|
|
|
return seniorSeller.buildings.create({name: 'Test Building'});
|
|
}).then(b => {
|
|
building = b;
|
|
debug('Building', b.toObject());
|
|
});
|
|
}
|
|
});
|