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.
- package/.circleci/config.yml +2 -2
- package/auth/index.js +45 -31
- package/auth/jwt.js +3 -2
- package/controllers/users.js +8 -1
- package/index.js +3 -7
- package/listeners.js +64 -59
- package/listeners_users.js +88 -87
- package/middleware/permissions.js +47 -53
- package/modules/admin/dist/index.html +1 -1
- package/modules/admin/dist/index.html.gz +0 -0
- package/modules/admin/dist/static/css/{chunk-5cee.9685afb8.css → chunk-5061.ce0e4bdb.css} +0 -0
- package/modules/admin/dist/static/css/{chunk-5cee.9685afb8.css.gz → chunk-5061.ce0e4bdb.css.gz} +0 -0
- package/modules/admin/dist/static/css/chunk-5e50.f67eeaed.css +1 -0
- package/modules/admin/dist/static/css/chunk-5e50.f67eeaed.css.gz +0 -0
- package/modules/admin/dist/static/css/{chunk-621e.f3d0eab6.css → chunk-621e.b826be8d.css} +0 -0
- package/modules/admin/dist/static/css/{chunk-621e.f3d0eab6.css.gz → chunk-621e.b826be8d.css.gz} +0 -0
- package/modules/admin/dist/static/css/chunk-7109.2bd6f37e.css +1 -0
- package/modules/admin/dist/static/css/chunk-7109.2bd6f37e.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-7f4f.f67eeaed.css +1 -0
- package/modules/admin/dist/static/css/chunk-7f4f.f67eeaed.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-9a95.c647a2ef.css +1 -0
- package/modules/admin/dist/static/css/chunk-9a95.c647a2ef.css.gz +0 -0
- package/modules/admin/dist/static/css/{chunk-bd5f.6ab011ba.css → chunk-bd5f.aefa49d7.css} +0 -0
- package/modules/admin/dist/static/css/{chunk-bd5f.6ab011ba.css.gz → chunk-bd5f.aefa49d7.css.gz} +0 -0
- package/modules/admin/dist/static/css/{chunk-e375.1d849865.css → chunk-e374.025d58bb.css} +0 -0
- package/modules/admin/dist/static/css/{chunk-e375.1d849865.css.gz → chunk-e374.025d58bb.css.gz} +0 -0
- package/modules/admin/dist/static/js/{app.ebe90d64.js → app.da5e53a7.js} +1 -1
- package/modules/admin/dist/static/js/app.da5e53a7.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-5061.83e6ccfa.js +1 -0
- package/modules/admin/dist/static/js/chunk-5061.83e6ccfa.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-5e17.a2b69611.js +1 -0
- package/modules/admin/dist/static/js/chunk-5e17.a2b69611.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-5e50.6820e69c.js +1 -0
- package/modules/admin/dist/static/js/chunk-5e50.6820e69c.js.gz +0 -0
- package/modules/admin/dist/static/js/{chunk-621e.6fe293e9.js → chunk-621e.ccf42393.js} +0 -0
- package/modules/admin/dist/static/js/{chunk-621e.6fe293e9.js.gz → chunk-621e.ccf42393.js.gz} +0 -0
- package/modules/admin/dist/static/js/chunk-7109.10cb4860.js +1 -0
- package/modules/admin/dist/static/js/chunk-7109.10cb4860.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-7f4f.ae4400a8.js +1 -0
- package/modules/admin/dist/static/js/chunk-7f4f.ae4400a8.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-9a95.bdd76583.js +1 -0
- package/modules/admin/dist/static/js/chunk-9a95.bdd76583.js.gz +0 -0
- package/modules/admin/dist/static/js/{chunk-bd5f.af5f2197.js → chunk-bd5f.e9509f10.js} +0 -0
- package/modules/admin/dist/static/js/{chunk-bd5f.af5f2197.js.gz → chunk-bd5f.e9509f10.js.gz} +0 -0
- package/modules/admin/dist/static/js/{chunk-e375.089d85cd.js → chunk-e374.7c87a0dd.js} +1 -1
- package/modules/admin/dist/static/js/chunk-e374.7c87a0dd.js.gz +0 -0
- package/modules/admin/package.json +1 -0
- package/modules/admin/src/router/index.js +200 -185
- package/modules/admin/src/views/Endpoints.vue +250 -0
- package/modules/admin/src/views/ListDocuments2.vue +465 -407
- package/modules/admin/src/views/ViewRequest.vue +7 -3
- package/modules/core/core.js +5 -0
- package/modules/logging/logging.js +3 -0
- package/package.json +1 -1
- package/test/0-install.js +1 -0
- package/test/users.js +37 -1
- package/util.js +18 -8
- package/modules/admin/dist/static/css/chunk-1dff.39c7634b.css +0 -1
- package/modules/admin/dist/static/css/chunk-1dff.39c7634b.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-3bc2.89632b9d.css +0 -1
- package/modules/admin/dist/static/css/chunk-3bc2.89632b9d.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-f691.4817ca73.css +0 -1
- package/modules/admin/dist/static/css/chunk-f691.4817ca73.css.gz +0 -0
- package/modules/admin/dist/static/js/app.ebe90d64.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-1dff.1f1c0999.js +0 -1
- package/modules/admin/dist/static/js/chunk-1dff.1f1c0999.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-3bc2.028748b7.js +0 -1
- package/modules/admin/dist/static/js/chunk-3bc2.028748b7.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-5cee.a6f0e769.js +0 -1
- package/modules/admin/dist/static/js/chunk-5cee.a6f0e769.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-e375.089d85cd.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-f691.c0cbf5c1.js +0 -1
- package/modules/admin/dist/static/js/chunk-f691.c0cbf5c1.js.gz +0 -0
package/.circleci/config.yml
CHANGED
package/auth/index.js
CHANGED
|
@@ -1,31 +1,45 @@
|
|
|
1
|
-
const bcrypt = require('bcryptjs')
|
|
2
|
-
const handler = require('./jwt')
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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,
|
package/controllers/users.js
CHANGED
|
@@ -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(
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
+
}
|
package/listeners_users.js
CHANGED
|
@@ -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
|
-
|
|
12
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
await api.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
await api.
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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,
|
|
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
|
|
File without changes
|
package/modules/admin/dist/static/css/{chunk-5cee.9685afb8.css.gz → chunk-5061.ce0e4bdb.css.gz}
RENAMED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.el-table[data-v-03853e97] th{background-color:#343a40!important;border-color:#454d55;color:#fff}
|
|
Binary file
|
|
File without changes
|
package/modules/admin/dist/static/css/{chunk-621e.f3d0eab6.css.gz → chunk-621e.b826be8d.css.gz}
RENAMED
|
File without changes
|
|
@@ -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}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.el-table[data-v-03853e97] th{background-color:#343a40!important;border-color:#454d55;color:#fff}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.side-panel[data-v-c8717050]{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}
|
|
Binary file
|
|
File without changes
|
package/modules/admin/dist/static/css/{chunk-bd5f.6ab011ba.css.gz → chunk-bd5f.aefa49d7.css.gz}
RENAMED
|
File without changes
|
|
File without changes
|
package/modules/admin/dist/static/css/{chunk-e375.1d849865.css.gz → chunk-e374.025d58bb.css.gz}
RENAMED
|
File without changes
|