import should from 'should/as-function'; import UserModel from '../models/user'; import TestHelper from "../test/helper"; describe('/user', () => { let server; before(done => TestHelper.before(done)); beforeEach(done => server = TestHelper.beforeEach(server, done)); afterEach(done => TestHelper.afterEach(server, done)); after(done => TestHelper.after(done)); describe('GET /users', () => { it('returns all users', done => { TestHelper.request(server, done, { method: 'get', url: '/users', auth: {basic: 'admin'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); const json = require('../test/db.json'); should(res.body).have.lengthOf(json.collections.users.length); should(res.body).matchEach(user => { should(user).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models', 'status'); should(user).have.property('_id').be.type('string'); should(user).have.property('email').be.type('string'); should(user).have.property('name').be.type('string'); should(user).have.property('level').be.type('string'); should(user).have.property('location').be.type('string'); should(user.devices).matchEach(device => { should(device).be.type('string'); }); should(user.models).matchEach(model => { should(model).be.type('string'); }); should(user).have.property('status').be.type('string'); }); done(); }); }); it('rejects requests from non-admins', done => { TestHelper.request(server, done, { method: 'get', url: '/users', auth: {basic: 'janedoe'}, httpStatus: 403 }); }); it('rejects requests from an admin API key', done => { TestHelper.request(server, done, { method: 'get', url: '/users', auth: {key: 'admin'}, httpStatus: 401 }); }); it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'get', url: '/users', httpStatus: 401 }); }); }); describe('GET /user/{name}', () => { it('returns own user details', done => { TestHelper.request(server, done, { method: 'get', url: '/user', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { if (err) return done (err); should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models'); should(res.body).have.property('_id').be.type('string'); should(res.body).have.property('email', 'jane.doe@bosch.com'); should(res.body).have.property('name', 'janedoe'); should(res.body).have.property('level', 'write'); should(res.body).have.property('location', 'Rng'); should(res.body).have.property('devices', ['Alpha I']); should(res.body).have.property('models', []); done(); }); }); it('returns other user details for admin', done => { TestHelper.request(server, done, { method: 'get', url: '/user/janedoe', auth: {basic: 'admin'}, httpStatus: 200 }).end((err, res) => { if (err) return done (err); should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models'); should(res.body).have.property('_id').be.type('string'); should(res.body).have.property('email', 'jane.doe@bosch.com'); should(res.body).have.property('name', 'janedoe'); should(res.body).have.property('level', 'write'); should(res.body).have.property('location', 'Rng'); should(res.body).have.property('devices', ['Alpha I']); should(res.body).have.property('models', []); done(); }); }); it('rejects requests from non-admins for another user', done => { TestHelper.request(server, done, { method: 'get', url: '/user/admin', auth: {basic: 'janedoe'}, httpStatus: 403 }); }); it('rejects requests from a user API key', done => { TestHelper.request(server, done, { method: 'get', url: '/user', auth: {key: 'janedoe'}, httpStatus: 401 }); }); it('rejects requests from an admin API key', done => { TestHelper.request(server, done, { method: 'get', url: '/user/janedoe', auth: {key: 'janedoe'}, httpStatus: 401 }); }); it('returns 404 for an unknown user', done => { TestHelper.request(server, done, { method: 'get', url: '/user/unknown', auth: {basic: 'admin'}, httpStatus: 404 }); }); it('rejects requests from an admin API key', done => { TestHelper.request(server, done, { method: 'get', url: '/user/janedoe', httpStatus: 401 }); }); }); describe('PUT /user/{name}', () => { it('returns own user details', done => { TestHelper.request(server, done, { method: 'put', url: '/user', auth: {basic: 'janedoe'}, httpStatus: 200, req: {} }).end((err, res) => { if (err) return done (err); should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models'); should(res.body).have.property('_id').be.type('string'); should(res.body).have.property('email', 'jane.doe@bosch.com'); should(res.body).have.property('name', 'janedoe'); should(res.body).have.property('level', 'write'); should(res.body).have.property('location', 'Rng'); should(res.body).have.property('devices', ['Alpha I']); should(res.body).have.property('models', []); done(); }); }); it('returns other user details for admin', done => { TestHelper.request(server, done, { method: 'put', url: '/user/janedoe', auth: {basic: 'admin'}, httpStatus: 200, req: {} }).end((err, res) => { if (err) return done (err); should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models'); should(res.body).have.property('_id').be.type('string'); should(res.body).have.property('email', 'jane.doe@bosch.com'); should(res.body).have.property('name', 'janedoe'); should(res.body).have.property('level', 'write'); should(res.body).have.property('location', 'Rng'); should(res.body).have.property('devices', ['Alpha I']); should(res.body).have.property('models', []); done(); }); }); it('changes user details as given', done => { TestHelper.request(server, done, { method: 'put', url: '/user', auth: {basic: 'admin'}, httpStatus: 200, req: {name: 'adminnew', email: 'adminnew@bosch.com', pass: 'Abc123##', location: 'Abt', devices: ['test'], models: ['120000000000000000000002']} }).end(err => { if (err) return done (err); UserModel.find({name: 'adminnew'}).lean().exec( (err, data) => { if (err) return done(err); should(data).have.lengthOf(1); should(data[0]).have.only.keys('_id', 'name', 'pass', 'email', 'level', 'location', 'devices', 'models', 'key', 'status', '__v'); should(data[0]).have.property('_id'); should(data[0]).have.property('name', 'adminnew'); should(data[0]).have.property('email', 'adminnew@bosch.com'); should(data[0]).have.property('pass').not.eql('Abc123##'); should(data[0]).have.property('level', 'admin'); should(data[0]).have.property('location', 'Abt'); should(data[0]).have.property('devices', ['test']); should(data[0].models[0].toString()).be.eql('120000000000000000000002'); should(data[0]).have.property('status', 'new'); done(); }); }); }); it('creates a changelog', done => { TestHelper.request(server, done, { method: 'put', url: '/user', auth: {basic: 'admin'}, httpStatus: 200, req: {name: 'adminnew', email: 'adminnew@bosch.com', pass: 'Abc123##', location: 'Abt', devices: ['test']}, log: { collection: 'users', dataIgn: ['pass'] } }); }); it('lets the admin change a user level', done => { TestHelper.request(server, done, { method: 'put', url: '/user/janedoe', auth: {basic: 'admin'}, httpStatus: 200, req: {level: 'read'} }).end(err => { if (err) return done (err); UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => { if (err) return done(err); should(data).have.lengthOf(1); should(data[0]).have.property('level', 'read'); done(); }); }); }); it('does not change the level', done => { TestHelper.request(server, done, { method: 'put', url: '/user', auth: {basic: 'janedoe'}, httpStatus: 400, default: false, req: {level: 'read'} }).end((err, res) => { if (err) return done (err); should(res.body).be.eql({status: 'Invalid body format', details: '"level" is not allowed'}); UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => { if (err) return done(err); should(data).have.lengthOf(1); should(data[0]).have.property('level', 'write'); done(); }); }); }); it('lets the admin change accessible models', done => { TestHelper.request(server, done, { method: 'put', url: '/user/janedoe', auth: {basic: 'admin'}, httpStatus: 200, req: {models: ['120000000000000000000001']} }).end(err => { if (err) return done (err); UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => { if (err) return done(err); should(data).have.lengthOf(1); should(data[0].models[0].toString()).be.eql('120000000000000000000001'); done(); }); }); }); it('does not change the models', done => { TestHelper.request(server, done, { method: 'put', url: '/user', auth: {basic: 'janedoe'}, httpStatus: 400, default: false, req: {models: ['120000000000000000000001']} }).end((err, res) => { if (err) return done (err); should(res.body).be.eql({status: 'Invalid body format', details: '"models" is not allowed'}); UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => { if (err) return done(err); should(data).have.lengthOf(1); should(data[0]).have.property('models', []); done(); }); }); }); it('rejects a username already in use', done => { TestHelper.request(server, done, { method: 'put', url: '/user', auth: {basic: 'admin'}, httpStatus: 400, default: false, req: {name: 'janedoe'} }).end((err, res) => { if (err) return done (err); should(res.body).be.eql({status: 'Username already taken'}); UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => { if (err) return done(err); should(data).have.lengthOf(1); done(); }); }); }); it('rejects a username which is in the special names', done => { TestHelper.request(server, done, { method: 'post', url: '/user/new', auth: {basic: 'admin'}, httpStatus: 400, default: false, req: {email: 'j.doe@bosch.com', name: 'passreset', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: []}, res: {status: 'Username already taken'} }); }); it('rejects invalid user details', done => { TestHelper.request(server, done, { method: 'put', url: '/user', auth: {basic: 'admin'}, httpStatus: 400, req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', location: 44, devices: ['Alpha II']}, res: {status: 'Invalid body format', details: '"location" must be a string'} }); }); it('rejects an invalid email address', done => { TestHelper.request(server, done, { method: 'put', url: '/user', auth: {basic: 'admin'}, httpStatus: 400, req: {email: 'john.doe'}, res: {status: 'Invalid body format', details: '"email" must be a valid email'} }); }); it('rejects an invalid password', done => { TestHelper.request(server, done, { method: 'put', url: '/user', auth: {basic: 'admin'}, httpStatus: 400, req: {pass: 'pass'}, res: {status: 'Invalid body format', details: '"pass" length must be at least 8 characters long'} }); }); it('rejects requests from non-admins for another user', done => { TestHelper.request(server, done, { method: 'put', url: '/user/admin', auth: {basic: 'janedoe'}, httpStatus: 403, req: {} }); }); it('rejects requests from a user API key', done => { TestHelper.request(server, done, { method: 'put', url: '/user', auth: {key: 'janedoe'}, httpStatus: 401, req: {} }); }); it('rejects requests from an admin API key', done => { TestHelper.request(server, done, { method: 'put', url: '/user/janedoe', auth: {key: 'admin'}, httpStatus: 401, req: {} }); }); it('returns 404 for an unknown user', done => { TestHelper.request(server, done, { method: 'put', url: '/user/unknown', auth: {basic: 'admin'}, httpStatus: 404, req: {} }); }); it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'put', url: '/user/janedoe', httpStatus: 401, req: {} }); }); }); describe('DELETE /user/{name}', () => { it('sets own user details to deleted', done => { TestHelper.request(server, done, { method: 'delete', url: '/user', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { if (err) return done (err); should(res.body).be.eql({status: 'OK'}); UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => { if (err) return done(err); should(data).have.lengthOf(1); should(data[0]).have.property('status', 'deleted'); done(); }); }); }); it('sets other user details to deleted for admin', done => { TestHelper.request(server, done, { method: 'delete', url: '/user/janedoe', auth: {basic: 'admin'}, httpStatus: 200 }).end((err, res) => { if (err) return done (err); should(res.body).be.eql({status: 'OK'}); UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => { if (err) return done(err); should(data).have.lengthOf(1); should(data[0]).have.property('status', 'deleted'); done(); }); }); }); it('creates a changelog', done => { TestHelper.request(server, done, { method: 'delete', url: '/user', auth: {basic: 'janedoe'}, httpStatus: 200, log: { collection: 'users', dataAdd: {status: 'deleted'} } }); }); it('rejects requests from non-admins for another user', done => { TestHelper.request(server, done, { method: 'delete', url: '/user/admin', auth: {basic: 'janedoe'}, httpStatus: 403 }); }); it('rejects requests from a user API key', done => { TestHelper.request(server, done, { method: 'delete', url: '/user', auth: {key: 'janedoe'}, httpStatus: 401 }); }); it('rejects requests from an admin API key', done => { TestHelper.request(server, done, { method: 'delete', url: '/user/janedoe', auth: {key: 'admin'}, httpStatus: 401 }); }); it('returns 404 for an unknown user', done => { TestHelper.request(server, done, { method: 'delete', url: '/user/unknown', auth: {basic: 'admin'}, httpStatus: 404 }); }); it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'delete', url: '/user/janedoe', httpStatus: 401 }); }); }); describe('PUT /user/restore/{name}', () => { it('sets the status', done => { TestHelper.request(server, done, { method: 'put', url: '/user/restore/customerold', auth: {basic: 'admin'}, httpStatus: 200, req: {} }).end((err, res) => { if (err) return done (err); should(res.body).be.eql({status: 'OK'}); UserModel.findOne({name: 'customerold'}).lean().exec((err, data: any) => { if (err) return done(err); should(data).have.property('status','new'); done(); }); }); }); it('rejects an API key', done => { TestHelper.request(server, done, { method: 'put', url: '/user/restore/customerold', auth: {key: 'admin'}, httpStatus: 401, req: {} }); }); it('rejects a write user', done => { TestHelper.request(server, done, { method: 'put', url: '/user/restore/customerold', auth: {basic: 'janedoe'}, httpStatus: 403, req: {} }); }); it('returns 404 for an unknown sample', done => { TestHelper.request(server, done, { method: 'put', url: '/user/restore/xxx', auth: {basic: 'admin'}, httpStatus: 404, req: {} }); }); it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'put', url: '/user/restore/customerold', httpStatus: 401, req: {} }); }); }); describe('GET /user/key', () => { it('returns the right API key', done => { TestHelper.request(server, done, { method: 'get', url: '/user/key', auth: {basic: 'janedoe'}, httpStatus: 200, res: {key: TestHelper.auth.janedoe.key} }); }); it('rejects requests from an API key', done => { TestHelper.request(server, done, { method: 'get', url: '/user/key', auth: {key: 'janedoe'}, httpStatus: 401 }); }); it('rejects requests from an API key', done => { TestHelper.request(server, done, { method: 'get', url: '/user/key', httpStatus: 401 }); }); }); describe('POST /user/new', () => { it('returns the added user data', done => { TestHelper.request(server, done, { method: 'post', url: '/user/new', auth: {basic: 'admin'}, httpStatus: 200, req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: ['120000000000000000000003']} }).end((err, res) => { if (err) return done (err); should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'devices', 'models'); should(res.body).have.property('_id').be.type('string'); should(res.body).have.property('email', 'john.doe@bosch.com'); should(res.body).have.property('name', 'johndoe'); should(res.body).have.property('level', 'read'); should(res.body).have.property('location', 'Rng'); should(res.body).have.property('devices', ['Alpha II']); should(res.body).have.property('models', ['120000000000000000000003']); done(); }); }); it('stores the data', done => { TestHelper.request(server, done, { method: 'post', url: '/user/new', auth: {basic: 'admin'}, httpStatus: 200, req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: ['120000000000000000000002']} }).end(err => { if (err) return done (err); UserModel.find({name: 'johndoe'}).lean().exec( (err, data) => { if (err) return done(err); should(data).have.lengthOf(1); should(data[0]).have.only.keys('_id', 'name', 'pass', 'email', 'level', 'location', 'devices', 'models', 'key', 'status', '__v'); should(data[0]).have.property('_id'); should(data[0]).have.property('name', 'johndoe'); should(data[0]).have.property('email', 'john.doe@bosch.com'); should(data[0]).have.property('pass').not.eql('Abc123!#'); should(data[0]).have.property('level', 'read'); should(data[0]).have.property('location', 'Rng'); should(data[0]).have.property('devices', ['Alpha II']); should(data[0].models.toString()).be.eql('120000000000000000000002'); should(data[0]).have.property('status', 'new'); done(); }); }); }); it('creates a changelog', done => { TestHelper.request(server, done, { method: 'post', url: '/user/new', auth: {basic: 'admin'}, httpStatus: 200, req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: ['120000000000000000000002']}, log: { collection: 'users', dataIgn: ['pass', 'key'], dataAdd: { status: 'new'} } }); }); it('rejects a username already in use', done => { TestHelper.request(server, done, { method: 'post', url: '/user/new', auth: {basic: 'admin'}, httpStatus: 400, default: false, req: {email: 'j.doe@bosch.com', name: 'janedoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: []} }).end((err, res) => { if (err) return done (err); should(res.body).be.eql({status: 'Username already taken'}); UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => { if (err) return done(err); should(data).have.lengthOf(1); done(); }); }); }); it('rejects a username which is in the special names', done => { TestHelper.request(server, done, { method: 'post', url: '/user/new', auth: {basic: 'admin'}, httpStatus: 400, default: false, req: {email: 'j.doe@bosch.com', name: 'passreset', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: []}, res: {status: 'Username already taken'} }); }); it('rejects invalid user details', done => { TestHelper.request(server, done, { method: 'post', url: '/user/new', auth: {basic: 'admin'}, httpStatus: 400, req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 44, devices: ['Alpha II']}, res: {status: 'Invalid body format', details: '"location" must be a string'} }); }); it('rejects an invalid user level', done => { TestHelper.request(server, done, { method: 'post', url: '/user/new', auth: {basic: 'admin'}, httpStatus: 400, req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'xxx', location: 'Rng', devices: ['Alpha II']}, res: {status: 'Invalid body format', details: '"level" must be one of [predict, read, write, dev, admin]'} }); }); it('rejects an invalid email address', done => { TestHelper.request(server, done, { method: 'post', url: '/user/new', auth: {basic: 'admin'}, httpStatus: 400, req: {email: 'john.doe', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II']}, res: {status: 'Invalid body format', details: '"email" must be a valid email'} }); }); it('rejects an invalid password', done => { TestHelper.request(server, done, { method: 'post', url: '/user/new', auth: {basic: 'admin'}, httpStatus: 400, req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'pass', level: 'read', location: 'Rng', devices: ['Alpha II']}, res: {status: 'Invalid body format', details: '"pass" length must be at least 8 characters long'} }); }); it('rejects an invalid model', done => { TestHelper.request(server, done, { method: 'post', url: '/user/new', auth: {basic: 'admin'}, httpStatus: 400, req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II'], models: ['120000000000000000000001', '000000000000000000000001']}, res: {status: 'Invalid model id'} }); }); it('rejects requests from non-admins', done => { TestHelper.request(server, done, { method: 'post', url: '/user/new', auth: {basic: 'janedoe'}, httpStatus: 403, req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II']} }); }); it('rejects requests from an admin API key', done => { TestHelper.request(server, done, { method: 'post', url: '/user/new', auth: {key: 'admin'}, httpStatus: 401, req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II']} }); }); it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'post', url: '/user/new', httpStatus: 401, req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', devices: ['Alpha II']} }); }); }); describe('POST /user/passreset', () => { it('returns the ok response', done => { TestHelper.request(server, done, { method: 'post', url: '/user/passreset', httpStatus: 200, req: {email: 'jane.doe@bosch.com', name: 'janedoe'}, res: {status: 'OK'} }); }); it('creates a changelog', done => { TestHelper.request(server, done, { method: 'post', url: '/user/passreset', httpStatus: 200, req: {email: 'jane.doe@bosch.com', name: 'janedoe'}, log: { collection: 'users', dataIgn: ['email', 'name', 'pass'] } }); }); it('returns 404 for wrong username/email combo', done => { TestHelper.request(server, done, { method: 'post', url: '/user/passreset', httpStatus: 404, req: {email: 'jane.doe@bosch.com', name: 'admin'} }); }); it('returns 404 for unknown username', done => { TestHelper.request(server, done, { method: 'post', url: '/user/passreset', httpStatus: 404, req: {email: 'jane.doe@bosch.com', name: 'username'} }); }); it('changes the user password', done => { UserModel.find({name: 'janedoe'}).lean().exec( (err, data: any) => { if (err) return done(err); const oldpass = data[0].pass; TestHelper.request(server, done, { method: 'post', url: '/user/passreset', httpStatus: 200, req: {email: 'jane.doe@bosch.com', name: 'janedoe'} }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({status: 'OK'}); UserModel.find({name: 'janedoe'}).lean().exec( (err, data: any) => { if (err) return done(err); should(data[0].pass).not.eql(oldpass); done(); }); }); }); }); }); });