added GET /user route
This commit is contained in:
parent
8bf408138f
commit
a64229d1dc
@ -1,5 +1,6 @@
|
||||
Id:
|
||||
type: string
|
||||
example: 5ea0450ed851c30a90e70894
|
||||
_Id:
|
||||
properties:
|
||||
_id:
|
||||
|
113
oas/user.yaml
113
oas/user.yaml
@ -1,6 +1,6 @@
|
||||
/users:
|
||||
get:
|
||||
summary: TODO lists all users
|
||||
summary: lists all users
|
||||
description: 'Auth: basic, levels: admin'
|
||||
tags:
|
||||
- /user
|
||||
@ -21,12 +21,10 @@
|
||||
$ref: 'oas.yaml#/components/responses/403'
|
||||
500:
|
||||
$ref: 'oas.yaml#/components/responses/500'
|
||||
/user/{name}:
|
||||
parameters:
|
||||
- $ref: 'oas.yaml#/components/parameters/Name'
|
||||
/user:
|
||||
get:
|
||||
summary: TODO list user details
|
||||
description: 'Auth: basic, levels: read, write, maintain, dev get their own information without a name property specified, level: admin can get any user using the name parameter'
|
||||
summary: list own user details
|
||||
description: 'Auth: basic, levels: read, write, maintain, admin'
|
||||
tags:
|
||||
- /user
|
||||
security:
|
||||
@ -37,8 +35,6 @@
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: 'oas.yaml#/components/schemas/User'
|
||||
400:
|
||||
$ref: 'oas.yaml#/components/responses/400'
|
||||
@ -52,7 +48,98 @@
|
||||
$ref: 'oas.yaml#/components/responses/500'
|
||||
put:
|
||||
summary: TODO change user details
|
||||
description: 'Auth: basic, levels: read, write, maintain, dev can change their own information (except level) without a name property specified, level: admin can change any user using the name parameter'
|
||||
description: 'Auth: basic, levels: read, write, maintain, admin'
|
||||
tags:
|
||||
- /user
|
||||
security:
|
||||
- BasicAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: 'oas.yaml#/components/schemas/_Id'
|
||||
- $ref: 'oas.yaml#/components/schemas/UserName'
|
||||
- $ref: 'oas.yaml#/components/schemas/Email'
|
||||
properties:
|
||||
pass:
|
||||
type: string
|
||||
writeOnly: true
|
||||
example: Abc123!#
|
||||
location:
|
||||
type: string
|
||||
example: Rng
|
||||
device_name:
|
||||
type: string
|
||||
example: Alpha II
|
||||
responses:
|
||||
200:
|
||||
description: user details
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: 'oas.yaml#/components/schemas/User'
|
||||
400:
|
||||
$ref: 'oas.yaml#/components/responses/400'
|
||||
401:
|
||||
$ref: 'oas.yaml#/components/responses/401'
|
||||
403:
|
||||
$ref: 'oas.yaml#/components/responses/403'
|
||||
404:
|
||||
$ref: 'oas.yaml#/components/responses/404'
|
||||
500:
|
||||
$ref: 'oas.yaml#/components/responses/500'
|
||||
delete:
|
||||
summary: TODO delete user
|
||||
description: 'Auth: basic, levels: read, write, maintain, admin'
|
||||
tags:
|
||||
- /user
|
||||
security:
|
||||
- BasicAuth: []
|
||||
responses:
|
||||
200:
|
||||
$ref: 'oas.yaml#/components/responses/Ok'
|
||||
400:
|
||||
$ref: 'oas.yaml#/components/responses/400'
|
||||
401:
|
||||
$ref: 'oas.yaml#/components/responses/401'
|
||||
403:
|
||||
$ref: 'oas.yaml#/components/responses/403'
|
||||
404:
|
||||
$ref: 'oas.yaml#/components/responses/404'
|
||||
500:
|
||||
$ref: 'oas.yaml#/components/responses/500'
|
||||
/user/{name}:
|
||||
parameters:
|
||||
- $ref: 'oas.yaml#/components/parameters/Name'
|
||||
get:
|
||||
summary: list user details
|
||||
description: 'Auth: basic, levels: admin'
|
||||
tags:
|
||||
- /user
|
||||
security:
|
||||
- BasicAuth: []
|
||||
responses:
|
||||
200:
|
||||
description: user details
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: 'oas.yaml#/components/schemas/User'
|
||||
400:
|
||||
$ref: 'oas.yaml#/components/responses/400'
|
||||
401:
|
||||
$ref: 'oas.yaml#/components/responses/401'
|
||||
403:
|
||||
$ref: 'oas.yaml#/components/responses/403'
|
||||
404:
|
||||
$ref: 'oas.yaml#/components/responses/404'
|
||||
500:
|
||||
$ref: 'oas.yaml#/components/responses/500'
|
||||
put:
|
||||
summary: TODO change user details
|
||||
description: 'Auth: basic, levels: admin'
|
||||
tags:
|
||||
- /user
|
||||
security:
|
||||
@ -69,8 +156,6 @@
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: 'oas.yaml#/components/schemas/User'
|
||||
400:
|
||||
$ref: 'oas.yaml#/components/responses/400'
|
||||
@ -84,7 +169,7 @@
|
||||
$ref: 'oas.yaml#/components/responses/500'
|
||||
delete:
|
||||
summary: TODO delete user
|
||||
description: 'Auth: basic, levels: read, write, maintain, dev can delete their own account, level: admin can delete any user using the name parameter'
|
||||
description: 'Auth: basic, levels: admin'
|
||||
tags:
|
||||
- /user
|
||||
security:
|
||||
@ -123,7 +208,7 @@
|
||||
$ref: 'oas.yaml#/components/responses/500'
|
||||
/user/new:
|
||||
post:
|
||||
summary: TODO add new user
|
||||
summary: add new user
|
||||
description: 'Auth: basic, levels: admin'
|
||||
tags:
|
||||
- /user
|
||||
@ -160,7 +245,7 @@
|
||||
$ref: 'oas.yaml#/components/responses/500'
|
||||
/user/passreset:
|
||||
post:
|
||||
summary: TODO reset password and send mail to restore
|
||||
summary: reset password and send mail to restore
|
||||
description: 'Auth: none'
|
||||
tags:
|
||||
- /user
|
||||
|
@ -35,7 +35,7 @@ export default class db {
|
||||
}
|
||||
|
||||
// connect to db
|
||||
mongoose.connect(connectionString, {useNewUrlParser: true, useUnifiedTopology: true}, err => {
|
||||
mongoose.connect(connectionString, {useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true}, err => {
|
||||
if (err) done(err);
|
||||
});
|
||||
mongoose.connection.on('error', console.error.bind(console, 'connection error:'));
|
||||
@ -89,7 +89,6 @@ export default class db {
|
||||
let loadCounter = 0; // count number of loaded collections to know when to return done()
|
||||
Object.keys(json.collections).forEach(collectionName => { // create each collection
|
||||
for(let i in json.collections[collectionName]) { // convert $oid fields to actual ObjectIds
|
||||
console.log(json.collections[collectionName][i]);
|
||||
Object.keys(json.collections[collectionName][i]).forEach(key => {
|
||||
json.collections[collectionName][i][key] = json.collections[collectionName][i][key].hasOwnProperty('$oid') ? mongoose.Types.ObjectId(json.collections[collectionName][i][key].$oid) : json.collections[collectionName][i][key];
|
||||
})
|
||||
|
@ -1,7 +1,7 @@
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
const UserSchema = new mongoose.Schema({
|
||||
name: String,
|
||||
name: {type: String, index: {unique: true}},
|
||||
email: String,
|
||||
pass: String,
|
||||
key: String,
|
||||
|
@ -3,7 +3,7 @@ import should from 'should/as-function';
|
||||
import db from '../db';
|
||||
|
||||
|
||||
describe('/', () => {
|
||||
describe('GET /', () => {
|
||||
let server;
|
||||
|
||||
before(done => {
|
||||
|
@ -4,7 +4,258 @@ import db from '../db';
|
||||
import UserModel from '../models/user';
|
||||
|
||||
|
||||
describe('/user/new', () => {
|
||||
describe('GET /users', () => {
|
||||
let server;
|
||||
|
||||
before(done => {
|
||||
process.env.port = '2999';
|
||||
process.env.NODE_ENV = 'test';
|
||||
db.connect('test', done);
|
||||
});
|
||||
beforeEach(done => {
|
||||
delete require.cache[require.resolve('../index')]; // prevent loading from cache
|
||||
server = require('../index');
|
||||
db.drop(err => { // reset database
|
||||
if (err) return done(err);
|
||||
db.loadJson(require('../test/db.json'), done);
|
||||
});
|
||||
});
|
||||
afterEach(done => {
|
||||
server.close(done);
|
||||
});
|
||||
it('returns all users', done => {
|
||||
supertest(server)
|
||||
.get('/users')
|
||||
.auth('admin', 'Abc123!#')
|
||||
.expect('Content-type', /json/)
|
||||
.expect(200)
|
||||
.end((err, res) => {
|
||||
if (err) 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', 'device_name');
|
||||
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).have.property('device_name').be.type('string');
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects requests from non-admins', done => {
|
||||
supertest(server)
|
||||
.get('/users')
|
||||
.auth('janedoe', 'Xyz890*)')
|
||||
.expect('Content-type', /json/)
|
||||
.expect(403)
|
||||
.end((err, res) => {
|
||||
if (err) done (err);
|
||||
should(res.body).be.eql({status: 'Forbidden'});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects requests from an admin API key', done => {
|
||||
supertest(server)
|
||||
.get('/users?key=5ea131671feb9c2ee0aafc9a')
|
||||
.expect('Content-type', /json/)
|
||||
.expect(401)
|
||||
.end((err, res) => {
|
||||
if (err) done (err);
|
||||
should(res.body).be.eql({status: 'Unauthorized'});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('GET /user/{name}', () => {
|
||||
let server;
|
||||
|
||||
before(done => {
|
||||
process.env.port = '2999';
|
||||
process.env.NODE_ENV = 'test';
|
||||
db.connect('test', done);
|
||||
});
|
||||
beforeEach(done => {
|
||||
delete require.cache[require.resolve('../index')]; // prevent loading from cache
|
||||
server = require('../index');
|
||||
db.drop(err => { // reset database
|
||||
if (err) return done(err);
|
||||
db.loadJson(require('../test/db.json'), done);
|
||||
});
|
||||
});
|
||||
afterEach(done => {
|
||||
server.close(done);
|
||||
});
|
||||
it('returns own user details', done => {
|
||||
supertest(server)
|
||||
.get('/user')
|
||||
.auth('janedoe', 'Xyz890*)')
|
||||
.expect('Content-type', /json/)
|
||||
.expect(200)
|
||||
.end((err, res) => {
|
||||
if (err) done (err);
|
||||
should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'device_name');
|
||||
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('device_name', 'Alpha I');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('returns other user details for admin', done => {
|
||||
supertest(server)
|
||||
.get('/user/janedoe')
|
||||
.auth('admin', 'Abc123!#')
|
||||
.expect('Content-type', /json/)
|
||||
.expect(200)
|
||||
.end((err, res) => {
|
||||
if (err) done (err);
|
||||
should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'device_name');
|
||||
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('device_name', 'Alpha I');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects requests from non-admins for another user', done => {
|
||||
supertest(server)
|
||||
.get('/user/admin')
|
||||
.auth('janedoe', 'Xyz890*)')
|
||||
.expect('Content-type', /json/)
|
||||
.expect(403)
|
||||
.end((err, res) => {
|
||||
if (err) done (err);
|
||||
should(res.body).be.eql({status: 'Forbidden'});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects requests from a user API key', done => {
|
||||
supertest(server)
|
||||
.get('/user?key=5ea0450ed851c30a90e70899')
|
||||
.expect('Content-type', /json/)
|
||||
.expect(401)
|
||||
.end((err, res) => {
|
||||
if (err) done (err);
|
||||
should(res.body).be.eql({status: 'Unauthorized'});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects requests from an admin API key', done => {
|
||||
supertest(server)
|
||||
.get('/user/janedoe?key=5ea131671feb9c2ee0aafc9a')
|
||||
.expect('Content-type', /json/)
|
||||
.expect(401)
|
||||
.end((err, res) => {
|
||||
if (err) done (err);
|
||||
should(res.body).be.eql({status: 'Unauthorized'});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('PUT /user/{name}', () => {
|
||||
let server;
|
||||
|
||||
before(done => {
|
||||
process.env.port = '2999';
|
||||
process.env.NODE_ENV = 'test';
|
||||
db.connect('test', done);
|
||||
});
|
||||
beforeEach(done => {
|
||||
delete require.cache[require.resolve('../index')]; // prevent loading from cache
|
||||
server = require('../index');
|
||||
db.drop(err => { // reset database
|
||||
if (err) return done(err);
|
||||
db.loadJson(require('../test/db.json'), done);
|
||||
});
|
||||
});
|
||||
afterEach(done => {
|
||||
server.close(done);
|
||||
});
|
||||
it('returns own user details', done => {
|
||||
supertest(server)
|
||||
.get('/user')
|
||||
.auth('janedoe', 'Xyz890*)')
|
||||
.expect('Content-type', /json/)
|
||||
.expect(200)
|
||||
.end((err, res) => {
|
||||
if (err) done (err);
|
||||
should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'device_name');
|
||||
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('device_name', 'Alpha I');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('returns other user details for admin', done => {
|
||||
supertest(server)
|
||||
.get('/user/janedoe')
|
||||
.auth('admin', 'Abc123!#')
|
||||
.expect('Content-type', /json/)
|
||||
.expect(200)
|
||||
.end((err, res) => {
|
||||
if (err) done (err);
|
||||
should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'device_name');
|
||||
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('device_name', 'Alpha I');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects requests from non-admins for another user', done => {
|
||||
supertest(server)
|
||||
.get('/user/admin')
|
||||
.auth('janedoe', 'Xyz890*)')
|
||||
.expect('Content-type', /json/)
|
||||
.expect(403)
|
||||
.end((err, res) => {
|
||||
if (err) done (err);
|
||||
should(res.body).be.eql({status: 'Forbidden'});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects requests from a user API key', done => {
|
||||
supertest(server)
|
||||
.get('/user?key=5ea0450ed851c30a90e70899')
|
||||
.expect('Content-type', /json/)
|
||||
.expect(401)
|
||||
.end((err, res) => {
|
||||
if (err) done (err);
|
||||
should(res.body).be.eql({status: 'Unauthorized'});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects requests from an admin API key', done => {
|
||||
supertest(server)
|
||||
.get('/user/janedoe?key=5ea131671feb9c2ee0aafc9a')
|
||||
.expect('Content-type', /json/)
|
||||
.expect(401)
|
||||
.end((err, res) => {
|
||||
if (err) done (err);
|
||||
should(res.body).be.eql({status: 'Unauthorized'});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('POST /user/new', () => {
|
||||
let server;
|
||||
|
||||
before(done => {
|
||||
@ -32,6 +283,7 @@ describe('/user/new', () => {
|
||||
.expect(200)
|
||||
.end((err, res) => {
|
||||
if (err) done (err);
|
||||
should(res.body).have.only.keys('_id', 'email', 'name', 'level', 'location', 'device_name');
|
||||
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');
|
||||
@ -83,7 +335,7 @@ describe('/user/new', () => {
|
||||
it('rejects requests from non-admins', done => {
|
||||
supertest(server)
|
||||
.post('/user/new')
|
||||
.auth('janedoe', 'Abc123!#')
|
||||
.auth('janedoe', 'Xyz890*)')
|
||||
.send({email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'})
|
||||
.expect('Content-type', /json/)
|
||||
.expect(403)
|
||||
@ -108,9 +360,7 @@ describe('/user/new', () => {
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
describe('/user/passreset', () => {
|
||||
describe('POST /user/passreset', () => {
|
||||
let server;
|
||||
|
||||
before(done => {
|
||||
@ -191,7 +441,6 @@ describe('/user/passreset', () => {
|
||||
should(res.body).be.eql({status: 'OK'});
|
||||
UserModel.find({name: 'janedoe'}).lean().exec( (err, data: any) => {
|
||||
if (err) return done(err);
|
||||
console.log(data);
|
||||
should(data[0].pass).not.eql(oldpass);
|
||||
done();
|
||||
});
|
||||
|
@ -8,7 +8,24 @@ import mail from '../helpers/mail';
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/users', (req, res) => {
|
||||
res.json({message: 'users up and running!'});
|
||||
if (!req.auth(res, ['admin'], 'basic')) return;
|
||||
|
||||
UserModel.find({}).lean().exec( (err, data:any) => {
|
||||
res.json(data.map(e => UserValidate.output(e)).filter(e => e !== null)); // validate all and filter null values from validation errors
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/user/:username*?', (req, res) => {
|
||||
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||
let username = req.authDetails.username;
|
||||
if (req.params.username !== undefined) {
|
||||
if (!req.auth(res, ['admin'], 'basic')) return;
|
||||
username = req.params.username;
|
||||
}
|
||||
|
||||
UserModel.findOne({name: username}).lean().exec( (err, data:any) => {
|
||||
res.json(UserValidate.output(data)); // validate all and filter null values from validation errors
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/user/new', (req, res, next) => {
|
||||
@ -22,7 +39,7 @@ router.post('/user/new', (req, res, next) => {
|
||||
}
|
||||
|
||||
// check that user does not already exist
|
||||
UserModel.find({name: user.name}).lean().exec( 'find', (err, data) => {
|
||||
UserModel.find({name: user.name}).lean().exec( (err, data:any) => {
|
||||
if (err) next(err);
|
||||
if (data.length > 0) {
|
||||
res.status(400).json({status: 'Username already taken'});
|
||||
|
@ -33,13 +33,14 @@ export default class UserValidate { // validate input for user
|
||||
}
|
||||
|
||||
static output (data) { // validate output from database for needed properties, strip everything else
|
||||
return joi.object({
|
||||
const {value, error} = joi.object({
|
||||
_id: joi.any(),
|
||||
name: joi.string(),
|
||||
email: joi.string(),
|
||||
level: joi.string(),
|
||||
location: joi.string(),
|
||||
device_name: joi.string()
|
||||
}).validate(data, {stripUnknown: true}).value;
|
||||
device_name: joi.string().allow('')
|
||||
}).validate(data, {stripUnknown: true})
|
||||
return error !== undefined? null : value;
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
"_id": {"$oid":"5ea0450ed851c30a90e70894"},
|
||||
"email": "jane.doe@bosch.com",
|
||||
"name": "janedoe",
|
||||
"pass": "$2a$10$i872o3qR5V3JnbDArD8Z.eDo.BNPDBaR7dUX9KSEtl9pUjLyucy2K",
|
||||
"pass": "$2a$10$di26XKF63OG0V00PL1kSK.ceCcTxDExBMOg.jkHiCnXcY7cN7DlPi",
|
||||
"level": "write",
|
||||
"location": "Rng",
|
||||
"device_name": "Alpha I",
|
||||
|
Reference in New Issue
Block a user