Skip to content

Commit 68293e8

Browse files
Merge pull request #434 from nmarklund10/nmarklund10/431-implement-get-all-users-v2
Nmarklund10/431 implement get all users v2
2 parents 36629db + e277cd3 commit 68293e8

File tree

10 files changed

+289
-91
lines changed

10 files changed

+289
-91
lines changed

sequelize/config.js

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,35 @@
1-
require('dotenv').config();
2-
const fs = require('fs');
3-
const rdsCa = fs.readFileSync('./rds-combined-ca-bundle.pem');
1+
require('dotenv').config()
2+
const tls = require('tls')
3+
const fs = require('fs')
4+
const rdsCa = fs.readFileSync('./rds-combined-ca-bundle.pem')
45

56
module.exports = {
6-
development: {
7-
database: process.env.DATABASE_NAME,
8-
username: process.env.DATABASE_USERNAME,
9-
password: process.env.DATABASE_PASSWORD,
10-
port: process.env.DATABASE_PORT,
11-
host: process.env.DATABASE_HOST,
12-
dialect: 'postgres',
13-
},
14-
production: {
15-
database: process.env.DATABASE_NAME,
16-
username: process.env.DATABASE_USERNAME,
17-
password: process.env.DATABASE_PASSWORD,
18-
port: process.env.DATABASE_PORT,
19-
host: process.env.DATABASE_HOST,
20-
dialect: 'postgres',
21-
dialectOptions: {
22-
ssl: {
23-
rejectUnauthorized: true,
24-
ca: [rdsCa],
25-
checkServerIdentity: (host, cert) => {
26-
const error = tls.checkServerIdentity(host, cert);
27-
if (error && !cert.subject.CN.endsWith('.rds.amazonaws.com')) {
28-
return error;
29-
}
30-
}
31-
}
32-
}
33-
},
34-
};
7+
development: {
8+
database: process.env.DATABASE_NAME,
9+
username: process.env.DATABASE_USERNAME,
10+
password: process.env.DATABASE_PASSWORD,
11+
port: process.env.DATABASE_PORT,
12+
host: process.env.DATABASE_HOST,
13+
dialect: 'postgres',
14+
},
15+
production: {
16+
database: process.env.DATABASE_NAME,
17+
username: process.env.DATABASE_USERNAME,
18+
password: process.env.DATABASE_PASSWORD,
19+
port: process.env.DATABASE_PORT,
20+
host: process.env.DATABASE_HOST,
21+
dialect: 'postgres',
22+
dialectOptions: {
23+
ssl: {
24+
rejectUnauthorized: true,
25+
ca: [rdsCa],
26+
checkServerIdentity: (host, cert) => {
27+
const error = tls.checkServerIdentity(host, cert)
28+
if (error && !cert.subject.CN.endsWith('.rds.amazonaws.com')) {
29+
return error
30+
}
31+
}
32+
}
33+
}
34+
},
35+
}

sequelize/data/user.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
[
22
{
33
"email": "homer.simpson@sfpp.com",
4+
"displayName": "Homer Simpson",
45
"password": "donuts",
5-
"roles": ["admin"]
6+
"roles": [
7+
"admin"
8+
]
69
},
710
{
811
"email": "test@test.test",
12+
"displayName": "Test User",
913
"password": "test",
10-
"roles": ["user"]
14+
"roles": [
15+
"user"
16+
]
1117
}
1218
]
Lines changed: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,48 @@
1-
'use strict';
1+
'use strict'
22
module.exports = {
3-
up: (queryInterface, Sequelize) => {
4-
return queryInterface.createTable('Users', {
5-
id: {
6-
type: Sequelize.UUID,
7-
primaryKey: true,
8-
allowNull: false,
9-
autoIncrement: false,
10-
},
11-
email: {
12-
allowNull: false,
13-
unique: true,
14-
type: Sequelize.STRING
15-
},
16-
password: {
17-
allowNull: false,
18-
type: Sequelize.STRING
19-
},
20-
salt: {
21-
type: Sequelize.STRING
22-
},
23-
token: {
24-
type: Sequelize.STRING
25-
},
26-
displayName: {
27-
type: Sequelize.STRING
28-
},
29-
phone: {
30-
type: Sequelize.STRING
31-
},
32-
attributes: {
33-
type: Sequelize.JSON,
34-
},
35-
createdAt: {
36-
allowNull: false,
37-
type: Sequelize.DATE
38-
},
39-
updatedAt: {
40-
allowNull: false,
41-
type: Sequelize.DATE
42-
}
43-
});
44-
},
45-
down: queryInterface => {
46-
return queryInterface.dropTable('Users');
47-
}
48-
};
3+
up: (queryInterface, Sequelize) => {
4+
return queryInterface.createTable('Users', {
5+
id: {
6+
type: Sequelize.UUID,
7+
primaryKey: true,
8+
allowNull: false,
9+
autoIncrement: false,
10+
},
11+
email: {
12+
allowNull: false,
13+
unique: true,
14+
type: Sequelize.STRING
15+
},
16+
password: {
17+
allowNull: false,
18+
type: Sequelize.STRING
19+
},
20+
salt: {
21+
type: Sequelize.STRING
22+
},
23+
token: {
24+
type: Sequelize.STRING
25+
},
26+
displayName: {
27+
type: Sequelize.STRING
28+
},
29+
phone: {
30+
type: Sequelize.STRING
31+
},
32+
attributes: {
33+
type: Sequelize.JSON,
34+
},
35+
createdAt: {
36+
allowNull: false,
37+
type: Sequelize.DATE
38+
},
39+
updatedAt: {
40+
allowNull: false,
41+
type: Sequelize.DATE
42+
}
43+
})
44+
},
45+
down: queryInterface => {
46+
return queryInterface.dropTable('Users')
47+
}
48+
}

src/api-docs/v2/swagger.json

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,8 +1229,7 @@
12291229
"get": {
12301230
"tags": [
12311231
"users",
1232-
"read-only",
1233-
"unimplemented"
1232+
"read-only"
12341233
],
12351234
"summary": "Retrieves all users.",
12361235
"description": "Retrieves all defined users in the application.",
@@ -1248,9 +1247,6 @@
12481247
}
12491248
}
12501249
},
1251-
"401": {
1252-
"$ref": "#/components/responses/Unauthorized"
1253-
},
12541250
"403": {
12551251
"$ref": "#/components/responses/Forbidden"
12561252
},

src/routes/v2/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import health from './health'
22
import security from './security'
3+
import users from './users'
34
// Exports object of routes we import above. Add to this if you're adding new routes.
45

56
const routeExports = {
67
health,
7-
security
8+
security,
9+
users
810
}
911

1012
module.exports = routeExports

src/routes/v2/security.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import { Router } from 'express'
2+
import rateLimit from 'express-rate-limit'
23
import validator from 'validator'
34
import jwt from 'jsonwebtoken'
45
import { ErrorResponse } from '../../utils/v2'
56

67
const router = new Router()
8+
const max = (process.env.NODE_ENV !== 'production') ? 50000 : 50
9+
const loginLimiter = rateLimit({
10+
windowMs: 60 * 60 * 1000,
11+
max,
12+
message: 'Too many login attempts for this IP. Please try again later.'
13+
})
714

8-
router.post('/authenticate', async (req, res) => {
15+
router.post('/authenticate', loginLimiter, async (req, res) => {
916
let response
1017
try {
1118
const { email, password } = req.body

src/routes/v2/users.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Router } from 'express'
2+
import { authMiddleware } from '../../utils/v2'
3+
4+
const router = new Router()
5+
6+
7+
router.get('/', authMiddleware, async (req, res) => {
8+
let response
9+
try {
10+
const users = await req.context.models.User.findAll({
11+
attributes: ['email', 'displayName']
12+
})
13+
14+
return res.status(200).json(users)
15+
} catch (e) {
16+
console.error(e)
17+
response.setCode(500)
18+
}
19+
20+
return res.status(response.getCode()).json(response.getMessage())
21+
})
22+
23+
export default router

src/tests/v2/user.routes.spec.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ describe(`User tests (v${VERSION})`, function() {
1818
})
1919
after(async function() {
2020
await testUser.destroyToken()
21+
process.env.BYPASS_LOGIN = true
2122
})
2223

2324
it('should login', function (done) {
@@ -64,4 +65,34 @@ describe(`User tests (v${VERSION})`, function() {
6465
done()
6566
})
6667
})
68+
it('should get all users', function(done) {
69+
request(app)
70+
.get(`/v${VERSION}/users`)
71+
.set('Accept', 'application/json')
72+
.set('token', token)
73+
.send()
74+
.expect('Content-Type', 'application/json; charset=utf-8')
75+
.expect(200)
76+
.end((err, res) => {
77+
if (err) return done(err)
78+
expect(res.body).to.deep.include.members([{email: testUser.user.email, displayName: testUser.user.displayName}])
79+
done()
80+
})
81+
})
82+
it('should not get all users', function(done) {
83+
process.env.BYPASS_LOGIN = false
84+
request(app)
85+
.get(`/v${VERSION}/users`)
86+
.set('Accept', 'application/json')
87+
.set('token', randomWords())
88+
.send()
89+
.expect('Content-Type', 'application/json; charset=utf-8')
90+
.expect(403)
91+
.end((err, res) => {
92+
if (err) return done(err)
93+
expect(res.body.statusCode).to.equal(403)
94+
expect(res.body.message).to.equal('Access not permitted')
95+
done()
96+
})
97+
})
6798
})

src/utils/login.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class Login {
88
constructor(version = (process.env.DEFAULT_API_VERSION || '1')) {
99
this.DEFAULT_API_VERSION = version
1010
this.role = randomWords()
11-
this.user = { email: `${randomWords()}@test.test`, password: 'Abcdefg12!', roles: [this.role] }
11+
this.user = { email: `${randomWords()}@test.test`.toLowerCase(), password: 'Abcdefg12!', displayName: randomWords(), roles: [this.role] }
1212
this.methods = [
1313
'GET',
1414
'POST',
@@ -71,11 +71,11 @@ class Login {
7171
* Creates a temp user for testing.
7272
*/
7373
async _createUser() {
74-
const user = await models.User.create({ email: this.user.email.toLowerCase(), password: this.user.password })
74+
const user = await models.User.create({ email: this.user.email, password: this.user.password, displayName: this.user.displayName })
7575
this.user.id = user.id
7676

7777
const e = await loadCasbin()
78-
await e.addRoleForUser(this.user.email.toLowerCase(), this.role)
78+
await e.addRoleForUser(this.user.email, this.role)
7979
// await models.UserRole.create({
8080
// ptype: 'g',
8181
// v0: this.email.toLowerCase(),

0 commit comments

Comments
 (0)