expressa 2.0.12 → 2.0.14

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 CHANGED
@@ -20,7 +20,7 @@
20
20
  "xdescribe": true
21
21
  },
22
22
  "parserOptions": {
23
- "ecmaVersion": 2022
23
+ "ecmaVersion": 12
24
24
  },
25
25
  "env": {
26
26
  "es6": true,
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']
@@ -1,52 +1,52 @@
1
- const auth = require('../auth')
2
- const util = require('../util')
3
- const collectionsApi = require('./collections')
4
- const permissions = require('../middleware/permissions')
5
-
6
- exports.login = async (req, collection) => {
7
- const password = req.body.password
8
-
9
- if (typeof req.body.email !== 'string') {
10
- throw new util.ApiError(400, 'email must be a string')
11
- }
12
-
13
- req.body.email = req.body.email.toLowerCase()
14
-
15
- // check if user exists
16
- const result = await req.db[collection].find({
17
- email: req.body.email
18
- })
19
- if (result.length === 0) {
20
- throw new util.ApiError(400, 'No ' + collection + ' found with this email.')
21
- }
22
- const user = result[0]
23
- if (!auth.isValidPassword(password, user.password)) {
24
- throw new util.ApiError(401, 'Incorrect password')
25
- }
26
- const jwt_options = req.settings.jwt_expires_in ? { expiresIn: req.settings.jwt_expires_in } : {}
27
- const payload = auth.doLogin({
28
- id: user._id,
29
- collection,
30
- timestamp: user.meta.password_last_updated_at,
31
- jwt_secret: req.getSetting('jwt_secret'),
32
- jwt_options
33
- })
34
- req.uid = user._id
35
- req.ucollection = collection
36
- req.user = user
37
- await permissions.addRolePermissionsAsync(req)
38
- payload.canUseAdmin = req.hasPermission('login to admin')
39
- return payload
40
- }
41
-
42
- exports.register = function (req, collection) {
43
- req.url = `/${collection}`
44
- req.params.collection = collection
45
- return collectionsApi.insert(req)
46
- }
47
-
48
- exports.getMe = function (req, collection) {
49
- req.params.collection = collection
50
- req.params.id = req.uid
51
- return collectionsApi.getById(req)
52
- }
1
+ const auth = require('../auth')
2
+ const util = require('../util')
3
+ const collectionsApi = require('./collections')
4
+ const permissions = require('../middleware/permissions')
5
+
6
+ exports.login = async (req, collection) => {
7
+ const password = req.body.password
8
+
9
+ if (typeof req.body.email !== 'string') {
10
+ throw new util.ApiError(400, 'email must be a string')
11
+ }
12
+
13
+ req.body.email = req.body.email.toLowerCase()
14
+
15
+ // check if user exists
16
+ const result = await req.db[collection].find({
17
+ email: req.body.email
18
+ })
19
+ if (result.length === 0) {
20
+ throw new util.ApiError(400, 'No ' + collection + ' found with this email.')
21
+ }
22
+ const user = result[0]
23
+ if (!auth.isValidPassword(password, user.password)) {
24
+ throw new util.ApiError(401, 'Incorrect password')
25
+ }
26
+ const jwt_options = req.settings.jwt_expires_in ? { expiresIn: req.settings.jwt_expires_in } : {}
27
+ const payload = auth.doLogin({
28
+ id: user._id,
29
+ collection,
30
+ timestamp: user.meta.password_last_updated_at,
31
+ jwt_secret: req.getSetting('jwt_secret'),
32
+ jwt_options
33
+ })
34
+ req.uid = user._id
35
+ req.ucollection = collection
36
+ req.user = user
37
+ await permissions.addRolePermissionsAsync(req)
38
+ payload.canUseAdmin = req.hasPermission('login to admin')
39
+ return payload
40
+ }
41
+
42
+ exports.register = function (req, collection) {
43
+ req.url = `/${collection}`
44
+ req.params.collection = collection
45
+ return collectionsApi.insert(req)
46
+ }
47
+
48
+ exports.getMe = function (req, collection) {
49
+ req.params.collection = collection
50
+ req.params.id = req.uid
51
+ return collectionsApi.getById(req)
52
+ }
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.12",
3
+ "version": "2.0.14",
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.16",
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))
package/util.js CHANGED
@@ -75,6 +75,8 @@ exports.getSchemaProperty = function(collection, path) {
75
75
  for (const part of parts) {
76
76
  if (obj.properties && obj.properties[part]) {
77
77
  obj = obj.properties[part]
78
+ } else if (obj.items && obj.items.properties && obj.items.properties[part]) {
79
+ obj = obj.items.properties[part]
78
80
  } else {
79
81
  return
80
82
  }