expressa 2.0.12 → 2.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +1 -1
- package/auth/index.js +26 -0
- package/index.js +1 -1
- package/modules/access_keys/access_keys.js +33 -0
- package/package.json +2 -2
- package/test/0-install.js +1 -1
- package/test/test.js +14 -0
- package/test/testutils.js +48 -0
package/.eslintrc.json
CHANGED
package/auth/index.js
CHANGED
|
@@ -17,6 +17,32 @@ exports.isValidPassword = function (password, hashedPassword) {
|
|
|
17
17
|
exports.doLogin = handler.doLogin
|
|
18
18
|
|
|
19
19
|
exports.middleware = async function authMiddleware(req, res, next) {
|
|
20
|
+
const key = req.query['access_key'] || req.headers['x-access-key']
|
|
21
|
+
if (key) {
|
|
22
|
+
if (typeof key == 'string' && req.db['access_keys']) {
|
|
23
|
+
const accessKeys = await req.db['access_keys'].find({ key: { $eq: key } }, 0, 1)
|
|
24
|
+
if (accessKeys && accessKeys.length > 0) {
|
|
25
|
+
const accessKey = accessKeys[0]
|
|
26
|
+
// check if access key is still valid
|
|
27
|
+
if (accessKey.expires_at >= new Date().toISOString()) {
|
|
28
|
+
let user
|
|
29
|
+
try {
|
|
30
|
+
user = await req.db[accessKey.user_collection].get(accessKey.user_id)
|
|
31
|
+
} catch (e) {
|
|
32
|
+
req.uerror = 'user no longer exists'
|
|
33
|
+
}
|
|
34
|
+
if (user && !req.uerror) {
|
|
35
|
+
req.uid = user._id
|
|
36
|
+
req.ucollection = 'users'
|
|
37
|
+
req.user = user
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
console.error('Non-string access key sent')
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
20
46
|
req.query = req.query || {}
|
|
21
47
|
const token = req.query['token'] || req.headers['x-access-token']
|
|
22
48
|
delete req.query['token']
|
package/index.js
CHANGED
|
@@ -183,7 +183,7 @@ module.exports.api = function (settings) {
|
|
|
183
183
|
(async function setup() {
|
|
184
184
|
await initCollections(router.db, router)
|
|
185
185
|
|
|
186
|
-
const modules = ['collections', 'core', 'logging', 'permissions']
|
|
186
|
+
const modules = ['collections', 'core', 'logging', 'permissions', 'access_keys']
|
|
187
187
|
router.modules = {}
|
|
188
188
|
for (const module of modules) {
|
|
189
189
|
router.modules[module] = require(`./modules/${module}/${module}`)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
exports.collections = function () {
|
|
2
|
+
const access_keys = {
|
|
3
|
+
_id: 'access_keys',
|
|
4
|
+
schema: {
|
|
5
|
+
type: 'object',
|
|
6
|
+
additionalProperties: false,
|
|
7
|
+
properties: {
|
|
8
|
+
user_collection: {
|
|
9
|
+
type: 'string'
|
|
10
|
+
},
|
|
11
|
+
user_id: {
|
|
12
|
+
type: 'string'
|
|
13
|
+
},
|
|
14
|
+
key: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
description: 'randomly generated secret'
|
|
17
|
+
},
|
|
18
|
+
expires_at: {
|
|
19
|
+
type: 'string'
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
required: [
|
|
23
|
+
'user_id',
|
|
24
|
+
'key',
|
|
25
|
+
'expires_at',
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
storage: 'file',
|
|
29
|
+
documentsHaveOwners: true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return [access_keys]
|
|
33
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expressa",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.13",
|
|
4
4
|
"description": "API framework using JSON schemas",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": "https://github.com/thomas4019/expressa",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"jfs": "^0.3.0",
|
|
18
18
|
"jsonwebtoken": "^9.0.0",
|
|
19
19
|
"mongo-query": "^0.5.7",
|
|
20
|
-
"mongo-query-to-postgres-jsonb": "^0.2.
|
|
20
|
+
"mongo-query-to-postgres-jsonb": "^0.2.17",
|
|
21
21
|
"mongo-querystring": "4.1.1",
|
|
22
22
|
"mongodb": "^3.5.7",
|
|
23
23
|
"on-finished": "^2.3.0",
|
package/test/0-install.js
CHANGED
|
@@ -21,7 +21,7 @@ describe('install flow', function () {
|
|
|
21
21
|
await request(app)
|
|
22
22
|
.post('/install')
|
|
23
23
|
.send({
|
|
24
|
-
modules: ['collections', 'core', 'logging', 'permissions'],
|
|
24
|
+
modules: ['collections', 'core', 'logging', 'permissions', 'access_keys'],
|
|
25
25
|
settings: {
|
|
26
26
|
jwt_secret: 'testing 123',
|
|
27
27
|
jwt_expire_on_password_change: true,
|
package/test/test.js
CHANGED
|
@@ -54,6 +54,20 @@ describe('General Tests:', () => {
|
|
|
54
54
|
.expect(200)
|
|
55
55
|
})
|
|
56
56
|
|
|
57
|
+
it('returns collections using a key', async function () {
|
|
58
|
+
const badKey = '123'
|
|
59
|
+
await request(app)
|
|
60
|
+
.get('/collection')
|
|
61
|
+
.set('x-access-key', badKey)
|
|
62
|
+
.expect(401)
|
|
63
|
+
|
|
64
|
+
const key = await testutils.getAccessKeyForUserWithPermissions(api, ['collection: view'])
|
|
65
|
+
await request(app)
|
|
66
|
+
.get('/collection')
|
|
67
|
+
.set('x-access-key', key)
|
|
68
|
+
.expect(200)
|
|
69
|
+
})
|
|
70
|
+
|
|
57
71
|
it('Sets headers', async function () {
|
|
58
72
|
const token = await testutils.getUserWithPermissions(api, ['collection: view'])
|
|
59
73
|
const res = await request(app)
|
package/test/testutils.js
CHANGED
|
@@ -81,6 +81,54 @@ exports.getUserWithPermissions = async function(api, permissions) {
|
|
|
81
81
|
user._id = loginRes.body.uid
|
|
82
82
|
return loginRes.body.token
|
|
83
83
|
}
|
|
84
|
+
|
|
85
|
+
exports.getAccessKeyForUserWithPermissions = async function(api, permissions) {
|
|
86
|
+
const service = request(exports.app)
|
|
87
|
+
if (typeof permissions === 'string') {
|
|
88
|
+
permissions = [permissions]
|
|
89
|
+
}
|
|
90
|
+
permissions = permissions || []
|
|
91
|
+
const permissionsMap = {}
|
|
92
|
+
permissions.forEach(function (permission) {
|
|
93
|
+
permissionsMap[permission] = true
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
const randId = randomstring.generate(12)
|
|
97
|
+
const roleName = 'role' + randId
|
|
98
|
+
const role = {
|
|
99
|
+
_id: roleName,
|
|
100
|
+
permissions: permissionsMap
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const roleRes = await service.post('/role')
|
|
104
|
+
.set('x-access-token', tokens.admin)
|
|
105
|
+
.send(role)
|
|
106
|
+
.expect(200)
|
|
107
|
+
|
|
108
|
+
const user = {
|
|
109
|
+
email: 'test' + randId + '@example.com',
|
|
110
|
+
password: '123',
|
|
111
|
+
}
|
|
112
|
+
const registerRes = await service.post('/users/register')
|
|
113
|
+
.send(user)
|
|
114
|
+
.expect(200)
|
|
115
|
+
|
|
116
|
+
const updateRes = await service.post(`/users/${registerRes.body.id}/update`)
|
|
117
|
+
.send({ $push: { roles: roleName } })
|
|
118
|
+
.set('x-access-token', tokens.admin)
|
|
119
|
+
.expect(200)
|
|
120
|
+
|
|
121
|
+
let accessKey = randomstring.generate(8)
|
|
122
|
+
await exports.api.db.access_keys.create({
|
|
123
|
+
user_collection: 'users',
|
|
124
|
+
user_id: registerRes.body.id,
|
|
125
|
+
key: accessKey,
|
|
126
|
+
expires_at: '2032-01-01T00:00:00'
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
return accessKey
|
|
130
|
+
}
|
|
131
|
+
|
|
84
132
|
exports.clone = util.clone
|
|
85
133
|
exports.sleep = function sleep(ms) {
|
|
86
134
|
return new Promise(resolve => setTimeout(resolve, ms))
|