expressa 1.2.4 → 1.4.0

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.
Files changed (73) hide show
  1. package/.circleci/config.yml +2 -2
  2. package/auth/index.js +45 -31
  3. package/auth/jwt.js +3 -2
  4. package/controllers/users.js +8 -1
  5. package/index.js +3 -7
  6. package/listeners.js +64 -59
  7. package/listeners_users.js +88 -87
  8. package/middleware/permissions.js +47 -53
  9. package/modules/admin/dist/index.html +1 -1
  10. package/modules/admin/dist/index.html.gz +0 -0
  11. package/modules/admin/dist/static/css/{chunk-5cee.9685afb8.css → chunk-5061.ce0e4bdb.css} +0 -0
  12. package/modules/admin/dist/static/css/{chunk-5cee.9685afb8.css.gz → chunk-5061.ce0e4bdb.css.gz} +0 -0
  13. package/modules/admin/dist/static/css/chunk-5e50.f67eeaed.css +1 -0
  14. package/modules/admin/dist/static/css/chunk-5e50.f67eeaed.css.gz +0 -0
  15. package/modules/admin/dist/static/css/{chunk-621e.f3d0eab6.css → chunk-621e.b826be8d.css} +0 -0
  16. package/modules/admin/dist/static/css/{chunk-621e.f3d0eab6.css.gz → chunk-621e.b826be8d.css.gz} +0 -0
  17. package/modules/admin/dist/static/css/chunk-7109.2bd6f37e.css +1 -0
  18. package/modules/admin/dist/static/css/chunk-7109.2bd6f37e.css.gz +0 -0
  19. package/modules/admin/dist/static/css/chunk-7f4f.f67eeaed.css +1 -0
  20. package/modules/admin/dist/static/css/chunk-7f4f.f67eeaed.css.gz +0 -0
  21. package/modules/admin/dist/static/css/chunk-9a95.c647a2ef.css +1 -0
  22. package/modules/admin/dist/static/css/chunk-9a95.c647a2ef.css.gz +0 -0
  23. package/modules/admin/dist/static/css/{chunk-bd5f.6ab011ba.css → chunk-bd5f.aefa49d7.css} +0 -0
  24. package/modules/admin/dist/static/css/{chunk-bd5f.6ab011ba.css.gz → chunk-bd5f.aefa49d7.css.gz} +0 -0
  25. package/modules/admin/dist/static/css/{chunk-e375.1d849865.css → chunk-e374.025d58bb.css} +0 -0
  26. package/modules/admin/dist/static/css/{chunk-e375.1d849865.css.gz → chunk-e374.025d58bb.css.gz} +0 -0
  27. package/modules/admin/dist/static/js/{app.ebe90d64.js → app.da5e53a7.js} +1 -1
  28. package/modules/admin/dist/static/js/app.da5e53a7.js.gz +0 -0
  29. package/modules/admin/dist/static/js/chunk-5061.83e6ccfa.js +1 -0
  30. package/modules/admin/dist/static/js/chunk-5061.83e6ccfa.js.gz +0 -0
  31. package/modules/admin/dist/static/js/chunk-5e17.a2b69611.js +1 -0
  32. package/modules/admin/dist/static/js/chunk-5e17.a2b69611.js.gz +0 -0
  33. package/modules/admin/dist/static/js/chunk-5e50.6820e69c.js +1 -0
  34. package/modules/admin/dist/static/js/chunk-5e50.6820e69c.js.gz +0 -0
  35. package/modules/admin/dist/static/js/{chunk-621e.6fe293e9.js → chunk-621e.ccf42393.js} +0 -0
  36. package/modules/admin/dist/static/js/{chunk-621e.6fe293e9.js.gz → chunk-621e.ccf42393.js.gz} +0 -0
  37. package/modules/admin/dist/static/js/chunk-7109.10cb4860.js +1 -0
  38. package/modules/admin/dist/static/js/chunk-7109.10cb4860.js.gz +0 -0
  39. package/modules/admin/dist/static/js/chunk-7f4f.ae4400a8.js +1 -0
  40. package/modules/admin/dist/static/js/chunk-7f4f.ae4400a8.js.gz +0 -0
  41. package/modules/admin/dist/static/js/chunk-9a95.bdd76583.js +1 -0
  42. package/modules/admin/dist/static/js/chunk-9a95.bdd76583.js.gz +0 -0
  43. package/modules/admin/dist/static/js/{chunk-bd5f.af5f2197.js → chunk-bd5f.e9509f10.js} +0 -0
  44. package/modules/admin/dist/static/js/{chunk-bd5f.af5f2197.js.gz → chunk-bd5f.e9509f10.js.gz} +0 -0
  45. package/modules/admin/dist/static/js/{chunk-e375.089d85cd.js → chunk-e374.7c87a0dd.js} +1 -1
  46. package/modules/admin/dist/static/js/chunk-e374.7c87a0dd.js.gz +0 -0
  47. package/modules/admin/package.json +1 -0
  48. package/modules/admin/src/router/index.js +200 -185
  49. package/modules/admin/src/views/Endpoints.vue +250 -0
  50. package/modules/admin/src/views/ListDocuments2.vue +465 -407
  51. package/modules/admin/src/views/ViewRequest.vue +7 -3
  52. package/modules/core/core.js +5 -0
  53. package/modules/logging/logging.js +3 -0
  54. package/package.json +1 -1
  55. package/test/0-install.js +1 -0
  56. package/test/users.js +37 -1
  57. package/util.js +18 -8
  58. package/modules/admin/dist/static/css/chunk-1dff.39c7634b.css +0 -1
  59. package/modules/admin/dist/static/css/chunk-1dff.39c7634b.css.gz +0 -0
  60. package/modules/admin/dist/static/css/chunk-3bc2.89632b9d.css +0 -1
  61. package/modules/admin/dist/static/css/chunk-3bc2.89632b9d.css.gz +0 -0
  62. package/modules/admin/dist/static/css/chunk-f691.4817ca73.css +0 -1
  63. package/modules/admin/dist/static/css/chunk-f691.4817ca73.css.gz +0 -0
  64. package/modules/admin/dist/static/js/app.ebe90d64.js.gz +0 -0
  65. package/modules/admin/dist/static/js/chunk-1dff.1f1c0999.js +0 -1
  66. package/modules/admin/dist/static/js/chunk-1dff.1f1c0999.js.gz +0 -0
  67. package/modules/admin/dist/static/js/chunk-3bc2.028748b7.js +0 -1
  68. package/modules/admin/dist/static/js/chunk-3bc2.028748b7.js.gz +0 -0
  69. package/modules/admin/dist/static/js/chunk-5cee.a6f0e769.js +0 -1
  70. package/modules/admin/dist/static/js/chunk-5cee.a6f0e769.js.gz +0 -0
  71. package/modules/admin/dist/static/js/chunk-e375.089d85cd.js.gz +0 -0
  72. package/modules/admin/dist/static/js/chunk-f691.c0cbf5c1.js +0 -1
  73. package/modules/admin/dist/static/js/chunk-f691.c0cbf5c1.js.gz +0 -0
@@ -6,8 +6,8 @@ jobs:
6
6
  steps:
7
7
  - checkout
8
8
  - run:
9
- name: update-npm
10
- command: 'sudo npm install -g npm@latest'
9
+ name: set npm registry
10
+ command: 'npm config set registry https://registry.npmjs.org/'
11
11
  - run:
12
12
  name: install-npm
13
13
  command: npm install
package/auth/index.js CHANGED
@@ -1,31 +1,45 @@
1
- const bcrypt = require('bcryptjs')
2
- const handler = require('./jwt')
3
-
4
- exports.createHash = function (password) {
5
- const salt = bcrypt.genSaltSync(10)
6
- return bcrypt.hashSync(password, salt)
7
- }
8
-
9
- exports.isValidPassword = function (password, hashedPassword) {
10
- return bcrypt.compareSync(password, hashedPassword)
11
- }
12
-
13
- exports.doLogin = handler.doLogin
14
-
15
- exports.middleware = async function authMiddleware(req, res, next) {
16
- try {
17
- req.query = req.query || {}
18
- const token = req.query['token'] || req.headers['x-access-token']
19
- delete req.query['token']
20
- delete req.headers['x-access-token']
21
- const user = await handler.isLoggedIn(token, req.getSetting('jwt_secret'))
22
- if (user) {
23
- req.uid = user._id
24
- req.ucollection = user.collection
25
- }
26
- }
27
- catch(e) {
28
- req.uerror = e.message === 'jwt expired' ? 'expired token' : 'jwt error'
29
- }
30
- next()
31
- }
1
+ const bcrypt = require('bcryptjs')
2
+ const handler = require('./jwt')
3
+ const util = require('../util')
4
+
5
+ exports.createHash = function (password) {
6
+ const salt = bcrypt.genSaltSync(10)
7
+ return bcrypt.hashSync(password, salt)
8
+ }
9
+
10
+ exports.isValidPassword = function (password, hashedPassword) {
11
+ return bcrypt.compareSync(password, hashedPassword)
12
+ }
13
+
14
+ exports.doLogin = handler.doLogin
15
+
16
+ exports.middleware = async function authMiddleware(req, res, next) {
17
+ req.query = req.query || {}
18
+ const token = req.query['token'] || req.headers['x-access-token']
19
+ delete req.query['token']
20
+ delete req.headers['x-access-token']
21
+ let payload
22
+ try {
23
+ payload = await handler.isLoggedIn(token, req.getSetting('jwt_secret'))
24
+ }
25
+ catch(e) {
26
+ req.uerror = e.message
27
+ }
28
+ if (payload) {
29
+ let user
30
+ try {
31
+ user = await req.db[payload.collection].get(payload._id)
32
+ } catch (e) {
33
+ req.uerror = 'user no longer exists'
34
+ }
35
+ if (req.getSetting('jwt_expire_on_password_change') && user.meta.password_last_updated_at !== payload.timestamp) {
36
+ req.uerror = 'jwt expired'
37
+ }
38
+ if (!req.uerror) {
39
+ req.uid = payload._id
40
+ req.ucollection = payload.collection
41
+ req.user = user
42
+ }
43
+ }
44
+ next()
45
+ }
package/auth/jwt.js CHANGED
@@ -4,13 +4,14 @@ const util = require('../util')
4
4
 
5
5
  // User document already validated, created, and saved to database
6
6
  // The id of that document is given.
7
- exports.doLogin = function (id, collection, jwt_secret, jwt_options = {}) {
7
+ exports.doLogin = function ({ id, collection, timestamp, jwt_secret, jwt_options = {} }) {
8
8
  if (!jwt_secret) {
9
9
  throw new util.ApiError(500, 'missing jwt_secret')
10
10
  }
11
11
  const token = jwt.sign({
12
12
  _id: id,
13
- collection
13
+ collection,
14
+ timestamp,
14
15
  }, jwt_secret, jwt_options)
15
16
  return {
16
17
  token: token,
@@ -22,9 +22,16 @@ exports.login = async (req, collection) => {
22
22
  throw new util.ApiError(401, 'Incorrect password')
23
23
  }
24
24
  const jwt_options = req.settings.jwt_expires_in ? { expiresIn: req.settings.jwt_expires_in } : {}
25
- const payload = auth.doLogin(user._id, collection, req.getSetting('jwt_secret'), jwt_options)
25
+ const payload = auth.doLogin({
26
+ id: user._id,
27
+ collection,
28
+ timestamp: user.meta.password_last_updated_at,
29
+ jwt_secret: req.getSetting('jwt_secret'),
30
+ jwt_options
31
+ })
26
32
  req.uid = user._id
27
33
  req.ucollection = collection
34
+ req.user = user
28
35
  await permissions.addRolePermissionsAsync(req)
29
36
  payload.canUseAdmin = req.hasPermission('login to admin')
30
37
  return payload
package/index.js CHANGED
@@ -93,7 +93,8 @@ function ph (requestHandler) {
93
93
  }
94
94
  req.returnedError = err
95
95
  res.status(err.status).send({
96
- error: err.result || err.message || err, tokenError: req.uerror
96
+ error: err.result || err.message || err,
97
+ tokenError: req.uerror ? (req.uerror === 'jwt expired' ? 'expired token' : 'jwt error') : undefined
97
98
  })
98
99
  }
99
100
  }
@@ -112,13 +113,8 @@ module.exports.api = function (settings) {
112
113
  get pgpool() {
113
114
  return util.getPgPool(router.settings.postgresql_uri)
114
115
  },
115
- ApiError: util.ApiError,
116
- generateDocumentId: util.generateDocumentId,
117
116
  doLogin: auth.doLogin,
118
- createPagination: util.createPagination,
119
- createPagePagination: util.createPagePagination,
120
- normalizeOrderBy: util.normalizeOrderBy,
121
- getDatabaseOffset: util.getDatabaseOffset,
117
+ ...util,
122
118
  }
123
119
  router.eventListeners = {}
124
120
 
package/listeners.js CHANGED
@@ -1,59 +1,64 @@
1
- const debug = require('debug')('expressa')
2
- const util = require('./util')
3
-
4
- module.exports = function (api) {
5
- /* Listeners to avoid the need for a server restart */
6
- api.addCollectionListener('changed', 'collection', async function setupCollectionStorage (req, collection, data) {
7
- if (!(data.storage === 'memory' && util.getPath(api.db[data._id], 'type') === 'memory')) {
8
- await api.setupCollectionDb(data)
9
- }
10
- debug('updated ' + data._id + ' collection storage')
11
- })
12
- api.addCollectionListener('delete', 'collection', function cleanupCollectionStorage (req, collection, data) {
13
- delete api.db[data._id]
14
- debug('removed ' + data._id + ' collection storage')
15
- })
16
- api.addCollectionListener('changed', 'settings', function updateInMemorySettings (req, collection, data) {
17
- // TODO: only reload if current environment is updated
18
- Object.assign(req.settings, data)
19
- })
20
-
21
- api.addListener(['put', 'post'], function updateMetadata (req, collection, data, { event }) {
22
- data.meta = data.meta || {}
23
- data.meta.updated = new Date().toISOString()
24
- if (event === 'post') {
25
- data.meta.created = new Date().toISOString()
26
- if (req.user) {
27
- data.meta.owner = req.user._id
28
- }
29
- }
30
- })
31
-
32
- api.addCollectionListener('get', 'schemas', function addMetaToSchema (req, collection, data) {
33
- const schema = data.schema
34
- schema.properties.meta = {
35
- type: 'object',
36
- propertyOrder: 2000,
37
- properties: {
38
- created: {
39
- type: 'string'
40
- },
41
- updated: {
42
- type: 'string'
43
- }
44
- }
45
- }
46
- if (data.documentsHaveOwners) {
47
- schema.properties.meta.properties.owner = {
48
- type: 'string',
49
- links: [
50
- {
51
- rel: view owner user',
52
- href: '/admin/#/edit/users/{{self}}',
53
- class: 'comment-link'
54
- }
55
- ]
56
- }
57
- }
58
- })
59
- }
1
+ const debug = require('debug')('expressa')
2
+ const util = require('./util')
3
+
4
+ module.exports = function (api) {
5
+ /* Listeners to avoid the need for a server restart */
6
+ api.addCollectionListener('changed', 'collection', async function setupCollectionStorage (req, collection, data) {
7
+ if (!(data.storage === 'memory' && util.getPath(api.db[data._id], 'type') === 'memory')) {
8
+ await api.setupCollectionDb(data)
9
+ }
10
+ debug('updated ' + data._id + ' collection storage')
11
+ })
12
+ api.addCollectionListener('delete', 'collection', function cleanupCollectionStorage (req, collection, data) {
13
+ delete api.db[data._id]
14
+ debug('removed ' + data._id + ' collection storage')
15
+ })
16
+ api.addCollectionListener('changed', 'settings', function updateInMemorySettings (req, collection, data) {
17
+ // TODO: only reload if current environment is updated
18
+ Object.assign(req.settings, data)
19
+ })
20
+
21
+ api.addListener(['put', 'post'], function updateMetadata (req, collection, data, { event }) {
22
+ data.meta = data.meta || {}
23
+ data.meta.updated = new Date().toISOString()
24
+ if (event === 'post') {
25
+ data.meta.created = new Date().toISOString()
26
+ if (req.user) {
27
+ if (req.hasPermission('login to admin')) {
28
+ data.meta.owner = data.meta.owner || req.user._id
29
+ }
30
+ else {
31
+ data.meta.owner = req.user._id
32
+ }
33
+ }
34
+ }
35
+ })
36
+
37
+ api.addCollectionListener('get', 'schemas', function addMetaToSchema (req, collection, data) {
38
+ const schema = data.schema
39
+ schema.properties.meta = {
40
+ type: 'object',
41
+ propertyOrder: 2000,
42
+ properties: {
43
+ created: {
44
+ type: 'string'
45
+ },
46
+ updated: {
47
+ type: 'string'
48
+ }
49
+ }
50
+ }
51
+ if (data.documentsHaveOwners) {
52
+ schema.properties.meta.properties.owner = {
53
+ type: 'string',
54
+ links: [
55
+ {
56
+ rel: '» view owner user',
57
+ href: '/admin/#/edit/users/{{self}}',
58
+ class: 'comment-link'
59
+ }
60
+ ]
61
+ }
62
+ }
63
+ })
64
+ }
@@ -1,87 +1,88 @@
1
- const auth = require('./auth')
2
- const util = require('./util.js')
3
- const debug = require('debug')('expressa')
4
-
5
- module.exports = async function(api) {
6
-
7
- const loginCollections = (await util.getLoginCollections(api)).map((coll) => coll._id)
8
-
9
- api.addCollectionListener(['post', 'put'], loginCollections, async function updatePassword(req, collection, data) {
10
- if (req.method === 'PUT') {
11
- const oldData = await api.db[collection].get(data._id)
12
- if (!data.password) {
13
- data.password = oldData.password // preserve password if not explicitly set
14
- }
15
- }
16
- if (data.password && data.password.length !== 60 && data.password[0] !== '$') {
17
- debug('hashing and replacing password in the user document.')
18
- data.password = auth.createHash(data.password)
19
- }
20
- })
21
-
22
- api.addCollectionListener('post', loginCollections, async function roleCreateCheck(req, collection, data) {
23
- if (!data.roles) {
24
- data.roles = []
25
- }
26
- // user special case to allow first user to be admin
27
- if (collection === 'users') {
28
- const hasUsers = (await api.db.users.find({}, 0, 1)).length === 1
29
- if (!hasUsers && !data.roles.includes('Admin')) {
30
- throw new util.ApiError(400, 'first user must have role Admin')
31
- }
32
- if (hasUsers && data.roles.length > 0 && !req.hasPermission('users: modify roles')) {
33
- throw new util.ApiError(400, 'insufficient permissions to create user with roles')
34
- }
35
- }
36
- else {
37
- if (data.roles.length > 0 && !req.hasPermission(`${collection}: modify roles`)) {
38
- throw new util.ApiError(400, `insufficient permissions to create ${collection} with roles`)
39
- }
40
- }
41
- })
42
-
43
- api.addLateCollectionListener('put', loginCollections, async function roleChangeCheck(req, collection, data) {
44
- if (!req.hasPermission(`${collection}: modify roles`)) {
45
- const oldData = await api.db[collection].get(data._id)
46
- data.roles = oldData.roles
47
- }
48
- })
49
-
50
- api.addCollectionListenerWithPriority(['get', 'changed', 'deleted'], loginCollections, 100, function hidePasswordHashes(req, collection, data) {
51
- if (!req.hasPermission(`${collection}: view hashed passwords`)) {
52
- debug(`deleting password because "${collection}: view hashed passwords"-permission is not set`)
53
- delete data.password
54
- }
55
- })
56
-
57
- api.addCollectionListener('post', loginCollections, async function userUniquenessCheck(req, collection, data) {
58
- const result = await api.db[collection].find({email: data.email})
59
- if (result.length > 0) {
60
- throw new util.ApiError(409, 'This email is already registered.')
61
- }
62
- })
63
-
64
- /* Mantain users-roles reference */
65
- api.addCollectionListener('changed', 'role', async function addRoleToUserSchema(req, collection, data) {
66
- for (const coll of loginCollections) {
67
- const doc = await api.db.collection.get(coll)
68
- if (!doc.schema.properties.roles.items.enum.includes(data._id)) {
69
- doc.schema.properties.roles.items.enum.push(data._id)
70
- await api.db.collection.update(coll, doc)
71
- await api.notify('changed', req, coll, doc)
72
- }
73
- }
74
- })
75
-
76
- api.addCollectionListener('deleted', 'role', async function removeRoleToUserSchema(req, collection, data) {
77
- for (const coll of loginCollections) {
78
- const doc = await api.db.collection.get(coll)
79
- const roles = doc.schema.properties.roles.items.enum
80
- if (roles.includes(data._id)) {
81
- roles.splice(roles.indexOf(data._id), 1)
82
- await api.db.collection.update(coll, doc)
83
- await api.notify('changed', req, coll, doc)
84
- }
85
- }
86
- })
87
- }
1
+ const auth = require('./auth')
2
+ const util = require('./util.js')
3
+ const debug = require('debug')('expressa')
4
+
5
+ module.exports = async function(api) {
6
+
7
+ const loginCollections = (await util.getLoginCollections(api)).map((coll) => coll._id)
8
+
9
+ api.addCollectionListener(['post', 'put'], loginCollections, async function updatePassword(req, collection, data) {
10
+ if (req.method === 'PUT') {
11
+ if (!data.password) {
12
+ const oldData = await api.db[collection].get(data._id)
13
+ data.password = oldData.password // preserve password if not explicitly set
14
+ }
15
+ }
16
+ if (data.password && data.password.length !== 60 && data.password[0] !== '$') {
17
+ debug('hashing and replacing password in the user document.')
18
+ data.password = auth.createHash(data.password)
19
+ data.meta.password_last_updated_at = new Date().toISOString()
20
+ }
21
+ })
22
+
23
+ api.addCollectionListener('post', loginCollections, async function roleCreateCheck(req, collection, data) {
24
+ if (!data.roles) {
25
+ data.roles = []
26
+ }
27
+ // user special case to allow first user to be admin
28
+ if (collection === 'users') {
29
+ const hasUsers = (await api.db.users.find({}, 0, 1)).length === 1
30
+ if (!hasUsers && !data.roles.includes('Admin')) {
31
+ throw new util.ApiError(400, 'first user must have role Admin')
32
+ }
33
+ if (hasUsers && data.roles.length > 0 && !req.hasPermission('users: modify roles')) {
34
+ throw new util.ApiError(400, 'insufficient permissions to create user with roles')
35
+ }
36
+ }
37
+ else {
38
+ if (data.roles.length > 0 && !req.hasPermission(`${collection}: modify roles`)) {
39
+ throw new util.ApiError(400, `insufficient permissions to create ${collection} with roles`)
40
+ }
41
+ }
42
+ })
43
+
44
+ api.addLateCollectionListener('put', loginCollections, async function roleChangeCheck(req, collection, data) {
45
+ if (!req.hasPermission(`${collection}: modify roles`)) {
46
+ const oldData = await api.db[collection].get(data._id)
47
+ data.roles = oldData.roles
48
+ }
49
+ })
50
+
51
+ api.addCollectionListenerWithPriority(['get', 'changed', 'deleted'], loginCollections, 100, function hidePasswordHashes(req, collection, data) {
52
+ if (!req.hasPermission(`${collection}: view hashed passwords`)) {
53
+ debug(`deleting password because "${collection}: view hashed passwords"-permission is not set`)
54
+ delete data.password
55
+ }
56
+ })
57
+
58
+ api.addCollectionListener('post', loginCollections, async function userUniquenessCheck(req, collection, data) {
59
+ const result = await api.db[collection].find({email: data.email})
60
+ if (result.length > 0) {
61
+ throw new util.ApiError(409, 'This email is already registered.')
62
+ }
63
+ })
64
+
65
+ /* Mantain users-roles reference */
66
+ api.addCollectionListener('changed', 'role', async function addRoleToUserSchema(req, collection, data) {
67
+ for (const coll of loginCollections) {
68
+ const doc = await api.db.collection.get(coll)
69
+ if (!doc.schema.properties.roles.items.enum.includes(data._id)) {
70
+ doc.schema.properties.roles.items.enum.push(data._id)
71
+ await api.db.collection.update(coll, doc)
72
+ await api.notify('changed', req, coll, doc)
73
+ }
74
+ }
75
+ })
76
+
77
+ api.addCollectionListener('deleted', 'role', async function removeRoleToUserSchema(req, collection, data) {
78
+ for (const coll of loginCollections) {
79
+ const doc = await api.db.collection.get(coll)
80
+ const roles = doc.schema.properties.roles.items.enum
81
+ if (roles.includes(data._id)) {
82
+ roles.splice(roles.indexOf(data._id), 1)
83
+ await api.db.collection.update(coll, doc)
84
+ await api.notify('changed', req, coll, doc)
85
+ }
86
+ }
87
+ })
88
+ }
@@ -1,53 +1,47 @@
1
- const util = require('../util')
2
- let authenticatedRoleExists
3
-
4
- async function addRolePermissions (req, roles) {
5
- req.permissions = req.permissions || {}
6
- const roleDocs = await Promise.all(roles.map((name) => req.db.role.get(name)))
7
- roleDocs.forEach((roleDoc) => {
8
- for (const permission in roleDoc.permissions) {
9
- if (roleDoc.permissions[permission]) {
10
- req.permissions[permission] = true
11
- }
12
- }
13
- })
14
- }
15
-
16
- async function doesAuthenticatedRoleExist(req) {
17
- if (typeof authenticatedRoleExists === 'undefined') {
18
- try {
19
- await req.db.role.get('Authenticated')
20
- authenticatedRoleExists = true
21
- } catch (e) {
22
- authenticatedRoleExists = false
23
- }
24
- }
25
- return authenticatedRoleExists
26
- }
27
-
28
- module.exports.addRolePermissionsAsync = async function addRolePermissionsMiddlewareAsync(req) {
29
- if (!req.settings || !req.settings.enforce_permissions) {
30
- // Use a dummy permission getter
31
- req.hasPermission = () => true
32
- return
33
- }
34
- req.hasPermission = (permission) => req.permissions && req.permissions[permission]
35
- let roles = ['Anonymous']
36
- const isAuthenticatedRole = await doesAuthenticatedRoleExist(req)
37
- if (req.uid) {
38
- try {
39
- const user = await req.db[req.ucollection].get(req.uid)
40
- req.user = user
41
- roles = (user.roles || []).concat(isAuthenticatedRole ? ['Authenticated'] : [])
42
- } catch (e) {
43
- throw new util.ApiError(404, 'User no longer exists')
44
- }
45
- } else {
46
- req.user = {}
47
- }
48
- await addRolePermissions(req, roles)
49
- }
50
-
51
- module.exports.middleware = function addRolePermissionsMiddleware (req, res, next) {
52
- module.exports.addRolePermissionsAsync(req).then(next).catch(next)
53
- }
1
+ const util = require('../util')
2
+ let authenticatedRoleExists
3
+
4
+ async function addRolePermissions (req, roles) {
5
+ req.permissions = req.permissions || {}
6
+ const roleDocs = await Promise.all(roles.map((name) => req.db.role.get(name)))
7
+ roleDocs.forEach((roleDoc) => {
8
+ for (const permission in roleDoc.permissions) {
9
+ if (roleDoc.permissions[permission]) {
10
+ req.permissions[permission] = true
11
+ }
12
+ }
13
+ })
14
+ }
15
+
16
+ async function doesAuthenticatedRoleExist(req) {
17
+ if (typeof authenticatedRoleExists === 'undefined') {
18
+ try {
19
+ await req.db.role.get('Authenticated')
20
+ authenticatedRoleExists = true
21
+ } catch (e) {
22
+ authenticatedRoleExists = false
23
+ }
24
+ }
25
+ return authenticatedRoleExists
26
+ }
27
+
28
+ module.exports.addRolePermissionsAsync = async function addRolePermissionsMiddlewareAsync(req) {
29
+ if (!req.settings || !req.settings.enforce_permissions) {
30
+ // Use a dummy permission getter
31
+ req.hasPermission = () => true
32
+ return
33
+ }
34
+ req.hasPermission = (permission) => req.permissions && req.permissions[permission]
35
+ let roles = ['Anonymous']
36
+ const isAuthenticatedRole = await doesAuthenticatedRoleExist(req)
37
+ if (req.uid) {
38
+ roles = (req.user.roles || []).concat(isAuthenticatedRole ? ['Authenticated'] : [])
39
+ } else {
40
+ req.user = {}
41
+ }
42
+ await addRolePermissions(req, roles)
43
+ }
44
+
45
+ module.exports.middleware = function addRolePermissionsMiddleware (req, res, next) {
46
+ module.exports.addRolePermissionsAsync(req).then(next).catch(next)
47
+ }
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><script crossorigin src=https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.min.js></script><script crossorigin src=https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-dom.min.js></script><link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel=stylesheet><script src=settings.js></script><title>Expressa Admin</title><link rel=icon href=/admin/favicon.ico><link href=/admin/static/css/chunk-elementUI.3d004d9f.css rel=stylesheet><link href=/admin/static/css/chunk-libs.327ad89e.css rel=stylesheet><link href=/admin/static/css/app.9dd57eea.css rel=stylesheet></head><body><div id=app></div><script>!function(e){function n(n){for(var r,c,a=n[0],f=n[1],i=n[2],h=0,s=[];h<a.length;h++)c=a[h],Object.prototype.hasOwnProperty.call(u,c)&&u[c]&&s.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);for(l&&l(n);s.length;)s.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var f=t[c];0!==u[f]&&(r=!1)}r&&(o.splice(n--,1),e=a(a.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var n=[];c[e]?n.push(c[e]):0!==c[e]&&{"chunk-621e":1,"chunk-e375":1,"chunk-3bc2":1,"chunk-5cee":1,"chunk-7291":1,"chunk-b423":1,"chunk-bd5f":1,"chunk-f075":1,"chunk-f691":1,"chunk-1dff":1}[e]&&n.push(c[e]=new Promise((function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-09d8":"31d6cfe0","chunk-621e":"f3d0eab6","chunk-e375":"1d849865","chunk-3bc2":"89632b9d","chunk-5cee":"9685afb8","chunk-7291":"13514e45","chunk-b423":"5f239333","chunk-bd5f":"6ab011ba","chunk-e7c3":"31d6cfe0","chunk-f075":"3a895a1e","chunk-f691":"4817ca73","chunk-1dff":"39c7634b"}[e]+".css",c=a.p+r,u=document.getElementsByTagName("link"),o=0;o<u.length;o++){var f=(h=u[o]).getAttribute("data-href")||h.getAttribute("href");if("stylesheet"===h.rel&&(f===r||f===c))return n()}var i=document.getElementsByTagName("style");for(o=0;o<i.length;o++){var h;if((f=(h=i[o]).getAttribute("data-href"))===r||f===c)return n()}var l=document.createElement("link");l.rel="stylesheet",l.type="text/css",l.onload=n,l.onerror=function(n){var r=n&&n.target&&n.target.src||c,u=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");u.request=r,t(u)},l.href=c,document.getElementsByTagName("head")[0].appendChild(l)})).then((function(){c[e]=0})));var t=u[e];if(0!==t)if(t)n.push(t[2]);else{var r=new Promise((function(n,r){t=u[e]=[n,r]}));n.push(t[2]=r);var o,f=document.createElement("script");f.charset="utf-8",f.timeout=120,a.nc&&f.setAttribute("nonce",a.nc),f.src=function(e){return a.p+"static/js/"+({}[e]||e)+"."+{"chunk-09d8":"561880e0","chunk-621e":"6fe293e9","chunk-e375":"089d85cd","chunk-3bc2":"028748b7","chunk-5cee":"a6f0e769","chunk-7291":"590524e9","chunk-b423":"387c504c","chunk-bd5f":"af5f2197","chunk-e7c3":"e5df8ceb","chunk-f075":"315ceec1","chunk-f691":"c0cbf5c1","chunk-1dff":"1f1c0999"}[e]+".js"}(e);var i=new Error;o=function(n){f.onerror=f.onload=null,clearTimeout(h);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src;i.message="Loading chunk "+e+" failed.\n("+r+": "+c+")",i.name="ChunkLoadError",i.type=r,i.request=c,t[1](i)}u[e]=void 0}};var h=setTimeout((function(){o({type:"timeout",target:f})}),12e4);f.onerror=f.onload=o,document.head.appendChild(f)}return Promise.all(n)},a.m=e,a.c=r,a.d=function(e,n,t){a.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,n){if(1&n&&(e=a(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)a.d(t,r,function(n){return e[n]}.bind(null,r));return t},a.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(n,"a",n),n},a.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},a.p="/admin/",a.oe=function(e){throw console.error(e),e};var f=window.webpackJsonp=window.webpackJsonp||[],i=f.push.bind(f);f.push=n,f=f.slice();for(var h=0;h<f.length;h++)n(f[h]);var l=i;t()}([]);</script><script src=/admin/static/js/chunk-elementUI.b0c99c08.js></script><script src=/admin/static/js/chunk-libs.06c38828.js></script><script src=/admin/static/js/app.ebe90d64.js></script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><script crossorigin src=https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.min.js></script><script crossorigin src=https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-dom.min.js></script><link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel=stylesheet><script src=settings.js></script><title>Expressa Admin</title><link rel=icon href=/admin/favicon.ico><link href=/admin/static/css/chunk-elementUI.3d004d9f.css rel=stylesheet><link href=/admin/static/css/chunk-libs.327ad89e.css rel=stylesheet><link href=/admin/static/css/app.9dd57eea.css rel=stylesheet></head><body><div id=app></div><script>!function(e){function n(n){for(var r,c,a=n[0],f=n[1],i=n[2],h=0,d=[];h<a.length;h++)c=a[h],Object.prototype.hasOwnProperty.call(u,c)&&u[c]&&d.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);for(l&&l(n);d.length;)d.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var f=t[c];0!==u[f]&&(r=!1)}r&&(o.splice(n--,1),e=a(a.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var n=[];c[e]?n.push(c[e]):0!==c[e]&&{"chunk-621e":1,"chunk-e374":1,"chunk-5061":1,"chunk-5e50":1,"chunk-7109":1,"chunk-7291":1,"chunk-7f4f":1,"chunk-9a95":1,"chunk-b423":1,"chunk-bd5f":1,"chunk-f075":1}[e]&&n.push(c[e]=new Promise((function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-09d8":"31d6cfe0","chunk-621e":"b826be8d","chunk-e374":"025d58bb","chunk-5061":"ce0e4bdb","chunk-5e17":"31d6cfe0","chunk-5e50":"f67eeaed","chunk-7109":"2bd6f37e","chunk-7291":"13514e45","chunk-7f4f":"f67eeaed","chunk-9a95":"c647a2ef","chunk-b423":"5f239333","chunk-bd5f":"aefa49d7","chunk-e7c3":"31d6cfe0","chunk-f075":"3a895a1e"}[e]+".css",c=a.p+r,u=document.getElementsByTagName("link"),o=0;o<u.length;o++){var f=(h=u[o]).getAttribute("data-href")||h.getAttribute("href");if("stylesheet"===h.rel&&(f===r||f===c))return n()}var i=document.getElementsByTagName("style");for(o=0;o<i.length;o++){var h;if((f=(h=i[o]).getAttribute("data-href"))===r||f===c)return n()}var l=document.createElement("link");l.rel="stylesheet",l.type="text/css",l.onload=n,l.onerror=function(n){var r=n&&n.target&&n.target.src||c,u=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");u.request=r,t(u)},l.href=c,document.getElementsByTagName("head")[0].appendChild(l)})).then((function(){c[e]=0})));var t=u[e];if(0!==t)if(t)n.push(t[2]);else{var r=new Promise((function(n,r){t=u[e]=[n,r]}));n.push(t[2]=r);var o,f=document.createElement("script");f.charset="utf-8",f.timeout=120,a.nc&&f.setAttribute("nonce",a.nc),f.src=function(e){return a.p+"static/js/"+({}[e]||e)+"."+{"chunk-09d8":"561880e0","chunk-621e":"ccf42393","chunk-e374":"7c87a0dd","chunk-5061":"83e6ccfa","chunk-5e17":"a2b69611","chunk-5e50":"6820e69c","chunk-7109":"10cb4860","chunk-7291":"590524e9","chunk-7f4f":"ae4400a8","chunk-9a95":"bdd76583","chunk-b423":"387c504c","chunk-bd5f":"e9509f10","chunk-e7c3":"e5df8ceb","chunk-f075":"315ceec1"}[e]+".js"}(e);var i=new Error;o=function(n){f.onerror=f.onload=null,clearTimeout(h);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src;i.message="Loading chunk "+e+" failed.\n("+r+": "+c+")",i.name="ChunkLoadError",i.type=r,i.request=c,t[1](i)}u[e]=void 0}};var h=setTimeout((function(){o({type:"timeout",target:f})}),12e4);f.onerror=f.onload=o,document.head.appendChild(f)}return Promise.all(n)},a.m=e,a.c=r,a.d=function(e,n,t){a.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,n){if(1&n&&(e=a(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)a.d(t,r,function(n){return e[n]}.bind(null,r));return t},a.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(n,"a",n),n},a.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},a.p="/admin/",a.oe=function(e){throw console.error(e),e};var f=window.webpackJsonp=window.webpackJsonp||[],i=f.push.bind(f);f.push=n,f=f.slice();for(var h=0;h<f.length;h++)n(f[h]);var l=i;t()}([]);</script><script src=/admin/static/js/chunk-elementUI.b0c99c08.js></script><script src=/admin/static/js/chunk-libs.06c38828.js></script><script src=/admin/static/js/app.da5e53a7.js></script></body></html>
Binary file
@@ -0,0 +1 @@
1
+ .el-table[data-v-03853e97] th{background-color:#343a40!important;border-color:#454d55;color:#fff}
@@ -0,0 +1 @@
1
+ .el-table[data-v-03853e97] th{background-color:#343a40!important;border-color:#454d55;color:#fff}.dashboard-container[data-v-5d163182]{margin:30px}.dashboard-text[data-v-5d163182]{font-size:30px;line-height:46px}a#settings-link[data-v-5d163182]{color:#35a;padding-left:25px}
@@ -0,0 +1 @@
1
+ .el-table[data-v-03853e97] th{background-color:#343a40!important;border-color:#454d55;color:#fff}
@@ -0,0 +1 @@
1
+ .side-panel[data-v-c8717050]{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}