Skip to content

Commit c985308

Browse files
committed
fix: use a public user object to prevent passwords and other secrets in user objects
1 parent b7b67a5 commit c985308

File tree

4 files changed

+83
-5
lines changed

4 files changed

+83
-5
lines changed

src/db/mongo/users.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const getUsers = async function (query: any = {}) {
1717
}
1818
console.log(`Getting users for query= ${JSON.stringify(query)}`);
1919
const collection = await connect(collectionName);
20-
return collection.find(query, { password: 0 }).toArray();
20+
return collection.find(query).project({ password: 0 }).toArray();
2121
};
2222

2323
export const deleteUser = async function (username: string) {

src/service/routes/users.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@ const express = require('express');
22
const router = new express.Router();
33
const db = require('../../db');
44

5+
const toPublicUser = (user) => {
6+
return {
7+
username: user.username || '',
8+
displayName: user.displayName || '',
9+
email: user.email || '',
10+
title: user.title || '',
11+
gitAccount: user.gitAccount || '',
12+
admin: user.admin || false,
13+
}
14+
}
15+
516
router.get('/', async (req, res) => {
617
const query = {};
718

@@ -17,16 +28,16 @@ router.get('/', async (req, res) => {
1728
query[k] = v;
1829
}
1930

20-
res.send(await db.getUsers(query));
31+
const users = await db.getUsers(query);
32+
res.send(users.map(toPublicUser));
2133
});
2234

2335
router.get('/:id', async (req, res) => {
2436
const username = req.params.id.toLowerCase();
2537
console.log(`Retrieving details for user: ${username}`);
2638
const data = await db.findUser(username);
2739
const user = JSON.parse(JSON.stringify(data));
28-
if (user && user.password) delete user.password;
29-
res.send(user);
40+
res.send(toPublicUser(user));
3041
});
3142

3243
module.exports = router;

test/services/routes/users.test.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
const chai = require('chai');
2+
const chaiHttp = require('chai-http');
3+
const sinon = require('sinon');
4+
const express = require('express');
5+
const usersRouter = require('../../../src/service/routes/users');
6+
const db = require('../../../src/db');
7+
8+
const { expect } = chai;
9+
chai.use(chaiHttp);
10+
11+
describe('Users API', function () {
12+
let app;
13+
14+
before(function () {
15+
app = express();
16+
app.use(express.json());
17+
app.use('/users', usersRouter);
18+
});
19+
20+
beforeEach(function () {
21+
sinon.stub(db, 'getUsers').resolves([
22+
{
23+
username: 'alice',
24+
password: 'secret-hashed-password',
25+
26+
displayName: 'Alice Walker',
27+
},
28+
]);
29+
sinon
30+
.stub(db, 'findUser')
31+
.resolves({ username: 'bob', password: 'hidden', email: '[email protected]' });
32+
});
33+
34+
afterEach(function () {
35+
sinon.restore();
36+
});
37+
38+
it('GET /users only serializes public data needed for ui, not user secrets like password', async function () {
39+
const res = await chai.request(app).get('/users');
40+
expect(res).to.have.status(200);
41+
expect(res.body).to.deep.equal([
42+
{
43+
username: 'alice',
44+
displayName: 'Alice Walker',
45+
46+
title: '',
47+
gitAccount: '',
48+
admin: false,
49+
},
50+
]);
51+
});
52+
53+
it('GET /users/:id does not serialize password', async function () {
54+
const res = await chai.request(app).get('/users/bob');
55+
expect(res).to.have.status(200);
56+
console.log(`Response body: ${res.body}`);
57+
58+
expect(res.body).to.deep.equal({
59+
username: 'bob',
60+
displayName: '',
61+
62+
title: '',
63+
gitAccount: '',
64+
admin: false,
65+
});
66+
});
67+
});

test/testDb.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ describe('Database clients', async () => {
241241
TEST_USER.admin,
242242
);
243243

244-
// has less properties to prove that records are merged
244+
// has fewer properties to prove that records are merged
245245
const updateToApply = {
246246
username: TEST_USER.username,
247247
gitAccount: 'updatedGitAccount',

0 commit comments

Comments
 (0)