expressa 1.4.7 → 2.0.1
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/controllers/collections.js +66 -8
- package/listeners.js +5 -10
- package/listeners_collection_permissions.js +1 -4
- package/listeners_users.js +92 -92
- package/listeners_validation.js +43 -45
- package/modules/collections/collections.js +10 -2
- package/modules/permissions/permissions.js +2 -8
- package/package.json +1 -1
- package/test/0-install.js +11 -0
- package/test/collections.querying.js +10 -10
- package/test/test.js +1 -1
- package/test/testutils.js +54 -2
- package/test/users.js +119 -0
- package/util.js +423 -461
package/.eslintrc.json
CHANGED
|
@@ -10,6 +10,55 @@ function assertValidCollection(req) {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
async function validateDocumentOwner(req, doc) {
|
|
14
|
+
if (!doc.meta.owner_collection) {
|
|
15
|
+
throw new util.ApiError(417, 'no owner_collection for owner found')
|
|
16
|
+
}
|
|
17
|
+
if (doc._id !== doc.meta.owner) {
|
|
18
|
+
const count = await req.db[doc.meta.owner_collection].count({ _id: doc.meta.owner }, undefined, 1)
|
|
19
|
+
if (!count) {
|
|
20
|
+
throw new util.ApiError(417, 'invalid owner')
|
|
21
|
+
}
|
|
22
|
+
} else if (req.params.collection !== doc.meta.owner_collection) {
|
|
23
|
+
throw new util.ApiError(417, 'invalid owner collection')
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function setDocumentOwner(req, doc) {
|
|
28
|
+
const schema = await req.db.collection.get(req.params.collection)
|
|
29
|
+
if (schema.documentsHaveOwners) {
|
|
30
|
+
util.addIdIfMissing(doc)
|
|
31
|
+
doc.meta ??= {}
|
|
32
|
+
if (req.uid) {
|
|
33
|
+
// request done by logged in user
|
|
34
|
+
if (!doc.meta.owner) {
|
|
35
|
+
// no owner - make logged in user as owner
|
|
36
|
+
doc.meta.owner = req.user._id
|
|
37
|
+
doc.meta.owner_collection = req.ucollection
|
|
38
|
+
} else {
|
|
39
|
+
if (!req.hasPermission(`${req.params.collection}: modify owner`)) {
|
|
40
|
+
// has an owner - but if no permission to modify owner then override owner to logged in user
|
|
41
|
+
doc.meta.owner = req.user._id
|
|
42
|
+
doc.meta.owner_collection = req.ucollection
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
// request done by NOT logged in user
|
|
47
|
+
if (schema.enableLogin) {
|
|
48
|
+
// is a user/login collection so make self owner
|
|
49
|
+
doc.meta.owner = doc._id
|
|
50
|
+
doc.meta.owner_collection = req.params.collection
|
|
51
|
+
} else {
|
|
52
|
+
// is a normal collection
|
|
53
|
+
debug('unable to find valid owner')
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (doc.meta.owner) {
|
|
57
|
+
await validateDocumentOwner(req, doc)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
13
62
|
exports.getSchema = async function (req) {
|
|
14
63
|
const collection = await req.db.collection.get(req.params.collection)
|
|
15
64
|
await util.notify('get', req, 'schemas', collection)
|
|
@@ -29,7 +78,8 @@ exports.get = async function (req) {
|
|
|
29
78
|
query = JSON.parse(req.query.query)
|
|
30
79
|
}
|
|
31
80
|
else {
|
|
32
|
-
const { skip, offset, limit, page, pageitems, pagemetadisable, orderby,
|
|
81
|
+
const { skip, offset, limit, page, pageitems, pagemetadisable, orderby, ...params } = req.query // eslint-disable-line no-unused-vars
|
|
82
|
+
delete params.fields
|
|
33
83
|
query = queryStringParser.parse(params)
|
|
34
84
|
}
|
|
35
85
|
if (req.query.orderby) {
|
|
@@ -41,7 +91,8 @@ exports.get = async function (req) {
|
|
|
41
91
|
// scenario where logged in user can only retrieve own docs
|
|
42
92
|
if (!req.hasPermission(`${req.params.collection}: view`) && req.hasPermission(`${req.params.collection}: view own`)) {
|
|
43
93
|
query['meta.owner'] = req.uid
|
|
44
|
-
fields
|
|
94
|
+
// if fields is null then all fields are requested
|
|
95
|
+
if (fields) fields['meta.owner'] = 1
|
|
45
96
|
}
|
|
46
97
|
}
|
|
47
98
|
|
|
@@ -94,7 +145,7 @@ exports.insert = async function (req) {
|
|
|
94
145
|
const data = req.body
|
|
95
146
|
req.customResponseData = req.customResponseData || {}
|
|
96
147
|
await util.notify('post', req, req.params.collection, data)
|
|
97
|
-
|
|
148
|
+
await setDocumentOwner(req, data)
|
|
98
149
|
const id = await req.db[req.params.collection].create(data)
|
|
99
150
|
await util.notify('changed', req, req.params.collection, data)
|
|
100
151
|
return {
|
|
@@ -145,16 +196,23 @@ exports.updateById = async function (req) {
|
|
|
145
196
|
|
|
146
197
|
const doc = await req.db[req.params.collection].get(req.params.id)
|
|
147
198
|
const owner = doc.meta && doc.meta.owner
|
|
199
|
+
const ownerCollection = doc.meta && doc.meta.owner_collection
|
|
148
200
|
util.mongoUpdate(doc, modifier)
|
|
149
201
|
const newOwner = doc.meta && doc.meta.owner
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
202
|
+
const newOwnerCollection = doc.meta && doc.meta.owner_collection
|
|
203
|
+
if (owner !== newOwner || ownerCollection !== newOwnerCollection) {
|
|
204
|
+
if (req.hasPermission(`${req.params.collection}: modify owner`)) {
|
|
205
|
+
if (owner) {
|
|
206
|
+
await validateDocumentOwner(req, doc)
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
debug('attempting to change document owner.')
|
|
210
|
+
doc.meta.owner = owner
|
|
211
|
+
doc.meta.owner_collection = ownerCollection
|
|
212
|
+
}
|
|
153
213
|
}
|
|
154
214
|
req.body = doc
|
|
155
215
|
await util.notify('put', req, req.params.collection, doc)
|
|
156
|
-
|
|
157
|
-
req.body.meta.owner = newOwner
|
|
158
216
|
await req.db[req.params.collection].update(req.params.id, req.body)
|
|
159
217
|
await util.notify('changed', req, req.params.collection, req.body)
|
|
160
218
|
if (Object.keys(req.customResponseData)) {
|
package/listeners.js
CHANGED
|
@@ -18,19 +18,11 @@ module.exports = function (api) {
|
|
|
18
18
|
Object.assign(req.settings, data)
|
|
19
19
|
})
|
|
20
20
|
|
|
21
|
-
api.addListener(['put', 'post'], function updateMetadata (req, collection, data, { event }) {
|
|
22
|
-
data.meta
|
|
21
|
+
api.addListener(['put', 'post'], async function updateMetadata (req, collection, data, { event }) {
|
|
22
|
+
data.meta ??= {}
|
|
23
23
|
data.meta.updated = new Date().toISOString()
|
|
24
24
|
if (event === 'post') {
|
|
25
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
26
|
}
|
|
35
27
|
})
|
|
36
28
|
|
|
@@ -63,6 +55,9 @@ module.exports = function (api) {
|
|
|
63
55
|
class: 'comment-link'
|
|
64
56
|
}
|
|
65
57
|
]
|
|
58
|
+
},
|
|
59
|
+
schema.properties.meta.properties.owner_collection = {
|
|
60
|
+
type: 'string'
|
|
66
61
|
}
|
|
67
62
|
}
|
|
68
63
|
}
|
|
@@ -19,11 +19,8 @@ module.exports = function (api) {
|
|
|
19
19
|
|
|
20
20
|
api.addListener(['get', 'put', 'post', 'delete'], function collectionPermissionCheck (req, collection, data, info) {
|
|
21
21
|
const permission = eventToPermissionMapping[info.event]
|
|
22
|
-
const editingSelf = collection === req.ucollection && data._id === req.uid
|
|
23
22
|
const editingOwnDoc = data.meta && data.meta.owner && data.meta.owner === req.uid
|
|
24
|
-
const editingOwn = (
|
|
25
|
-
req.hasPermission(collection + ': ' + permission + ' own'))
|
|
26
|
-
// console.log(editingOwn + ' ' + editingSelf + ' ' + editingOwnDoc);
|
|
23
|
+
const editingOwn = editingOwnDoc && req.hasPermission(collection + ': ' + permission + ' own')
|
|
27
24
|
if (!editingOwn && !req.hasPermission(collection + ': ' + permission)) {
|
|
28
25
|
debug(`cancelling, missing permission "${collection}: ${permission}"`)
|
|
29
26
|
throw new util.ApiError(401, 'You do not have permission to perform this action.')
|
package/listeners_users.js
CHANGED
|
@@ -1,92 +1,92 @@
|
|
|
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', 'put'], loginCollections, async function userEmailLowerCase(req, collection, data) {
|
|
59
|
-
data.email = data.email.toLowerCase()
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
api.addCollectionListener('post', loginCollections, async function userUniquenessCheck(req, collection, data) {
|
|
63
|
-
const result = await api.db[collection].find({email: data.email})
|
|
64
|
-
if (result.length > 0) {
|
|
65
|
-
throw new util.ApiError(409, 'This email is already registered.')
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
/* Mantain users-roles reference */
|
|
70
|
-
api.addCollectionListener('changed', 'role', async function addRoleToUserSchema(req, collection, data) {
|
|
71
|
-
for (const coll of loginCollections) {
|
|
72
|
-
const doc = await api.db.collection.get(coll)
|
|
73
|
-
if (!doc.schema.properties.roles.items.enum.includes(data._id)) {
|
|
74
|
-
doc.schema.properties.roles.items.enum.push(data._id)
|
|
75
|
-
await api.db.collection.update(coll, doc)
|
|
76
|
-
await api.notify('changed', req,
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
api.addCollectionListener('deleted', 'role', async function removeRoleToUserSchema(req, collection, data) {
|
|
82
|
-
for (const coll of loginCollections) {
|
|
83
|
-
const doc = await api.db.collection.get(coll)
|
|
84
|
-
const roles = doc.schema.properties.roles.items.enum
|
|
85
|
-
if (roles.includes(data._id)) {
|
|
86
|
-
roles.splice(roles.indexOf(data._id), 1)
|
|
87
|
-
await api.db.collection.update(coll, doc)
|
|
88
|
-
await api.notify('changed', req,
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
})
|
|
92
|
-
}
|
|
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', 'put'], loginCollections, async function userEmailLowerCase(req, collection, data) {
|
|
59
|
+
data.email = data.email.toLowerCase()
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
api.addCollectionListener('post', loginCollections, async function userUniquenessCheck(req, collection, data) {
|
|
63
|
+
const result = await api.db[collection].find({email: data.email})
|
|
64
|
+
if (result.length > 0) {
|
|
65
|
+
throw new util.ApiError(409, 'This email is already registered.')
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
/* Mantain users-roles reference */
|
|
70
|
+
api.addCollectionListener('changed', 'role', async function addRoleToUserSchema(req, collection, data) {
|
|
71
|
+
for (const coll of loginCollections) {
|
|
72
|
+
const doc = await api.db.collection.get(coll)
|
|
73
|
+
if (!doc.schema.properties.roles.items.enum.includes(data._id)) {
|
|
74
|
+
doc.schema.properties.roles.items.enum.push(data._id)
|
|
75
|
+
await api.db.collection.update(coll, doc)
|
|
76
|
+
await api.notify('changed', req, 'collection', doc)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
api.addCollectionListener('deleted', 'role', async function removeRoleToUserSchema(req, collection, data) {
|
|
82
|
+
for (const coll of loginCollections) {
|
|
83
|
+
const doc = await api.db.collection.get(coll)
|
|
84
|
+
const roles = doc.schema.properties.roles.items.enum
|
|
85
|
+
if (roles.includes(data._id)) {
|
|
86
|
+
roles.splice(roles.indexOf(data._id), 1)
|
|
87
|
+
await api.db.collection.update(coll, doc)
|
|
88
|
+
await api.notify('changed', req, 'collection', doc)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
}
|
package/listeners_validation.js
CHANGED
|
@@ -1,45 +1,43 @@
|
|
|
1
|
-
const util = require('./util')
|
|
2
|
-
|
|
3
|
-
function addImplicitFields (schema) {
|
|
4
|
-
schema['properties']
|
|
5
|
-
schema['properties']['meta']
|
|
6
|
-
schema['properties']['meta']['properties']
|
|
7
|
-
schema['properties']['meta']['properties']['created']
|
|
8
|
-
|
|
9
|
-
schema['properties']['meta']['properties']['
|
|
10
|
-
|
|
11
|
-
schema['properties']['
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
}
|
|
1
|
+
const util = require('./util')
|
|
2
|
+
|
|
3
|
+
function addImplicitFields (schema) {
|
|
4
|
+
schema['properties'] ??= {}
|
|
5
|
+
schema['properties']['meta'] ??= { type: 'object' }
|
|
6
|
+
schema['properties']['meta']['properties'] ??= {}
|
|
7
|
+
schema['properties']['meta']['properties']['created'] ??= { type: 'string' }
|
|
8
|
+
schema['properties']['meta']['properties']['updated'] ??= { type: 'string' }
|
|
9
|
+
schema['properties']['meta']['properties']['owner'] ??= { type: 'string' }
|
|
10
|
+
schema['properties']['meta']['properties']['owner_collection'] ??= { type: 'string' }
|
|
11
|
+
schema['properties']['_id'] ??= { type: 'string' }
|
|
12
|
+
return schema
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = async function (api) {
|
|
16
|
+
|
|
17
|
+
// Load all validators
|
|
18
|
+
const collections = await api.db.collection.all()
|
|
19
|
+
collections.forEach((collection) => {
|
|
20
|
+
const schema = addImplicitFields(collection.schema)
|
|
21
|
+
util.addSchemaValidator(collection._id, schema)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
// Load new and update validators as necessary
|
|
25
|
+
api.addCollectionListener('changed', 'collection', function updateSchemaValidators (req, collection, data) {
|
|
26
|
+
const schema = addImplicitFields(data.schema)
|
|
27
|
+
util.addSchemaValidator(data._id, schema)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
api.addCollectionListener('get', ['collection', 'schemas'], function ensureIdAdded (req, collection, data) {
|
|
31
|
+
if (data.schema) {
|
|
32
|
+
data.schema = addImplicitFields(data.schema)
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
api.addListener(['put', 'post'], function matchesSchema (req, collection, data) {
|
|
37
|
+
// TODO (switch to check if "installed" after install tests are done.
|
|
38
|
+
if (!req.settings.enforce_permissions) {
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
util.validateSchema(collection, data)
|
|
42
|
+
})
|
|
43
|
+
}
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
function getCollectionPermissions (name, hasOwner) {
|
|
2
|
-
let permissions =
|
|
2
|
+
let permissions = exports.collectionPermissions(name)
|
|
3
3
|
if (hasOwner) {
|
|
4
|
-
permissions = permissions.concat(
|
|
4
|
+
permissions = permissions.concat(exports.collectionOwnerPermissions(name))
|
|
5
5
|
}
|
|
6
6
|
return permissions
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
exports.collectionPermissions = function (name) {
|
|
10
|
+
return ['create', 'view', 'edit', 'modify owner', 'delete'].map((action) => `${name}: ${action}`)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
exports.collectionOwnerPermissions = function (name) {
|
|
14
|
+
return ['view', 'edit', 'delete'].map((action) => `${name}: ${action} own`)
|
|
15
|
+
}
|
|
16
|
+
|
|
9
17
|
exports.permissions = async function (app) {
|
|
10
18
|
const collections = (await app.db.collection.all()).map((collection) => collection._id)
|
|
11
19
|
// flatten the permissions into a single list
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const { collectionPermissions, collectionOwnerPermissions } = require('../collections/collections')
|
|
2
|
+
|
|
1
3
|
exports.settingSchema = {
|
|
2
4
|
enforce_permissions: {
|
|
3
5
|
type: 'boolean',
|
|
@@ -57,14 +59,6 @@ exports.install = async function (app) {
|
|
|
57
59
|
|
|
58
60
|
exports.permissions = ['users: modify roles', 'login to admin']
|
|
59
61
|
|
|
60
|
-
function collectionPermissions (name) {
|
|
61
|
-
return ['create', 'view', 'edit', 'delete'].map((action) => `${name}: ${action}`)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function collectionOwnerPermissions (name) {
|
|
65
|
-
return ['view', 'edit', 'delete'].map((action) => `${name}: ${action} own`)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
62
|
exports.init = async function (api) {
|
|
69
63
|
api.addCollectionListener(['changed'], 'collection', async function addCollectionPerms (req, collection, data) {
|
|
70
64
|
if (api.db.role) {
|
package/package.json
CHANGED
package/test/0-install.js
CHANGED
|
@@ -63,6 +63,17 @@ describe('install flow', function () {
|
|
|
63
63
|
expect(u.roles || []).to.include('Admin')
|
|
64
64
|
})
|
|
65
65
|
|
|
66
|
+
it('can login admin account', async function() {
|
|
67
|
+
const res = await request(app)
|
|
68
|
+
.post('/users/login')
|
|
69
|
+
.send({
|
|
70
|
+
email: 'a@example.com',
|
|
71
|
+
password: '123'
|
|
72
|
+
})
|
|
73
|
+
.expect(200)
|
|
74
|
+
testutils.setAdminToken(res.body.token)
|
|
75
|
+
})
|
|
76
|
+
|
|
66
77
|
it('2nd account cannot be admin account', async function() {
|
|
67
78
|
await request(app)
|
|
68
79
|
.post('/users/register')
|
|
@@ -11,7 +11,7 @@ const { app, api } = testutils
|
|
|
11
11
|
*/
|
|
12
12
|
describe('querying collections', function () {
|
|
13
13
|
it('create doc 1', async function () {
|
|
14
|
-
const token = await
|
|
14
|
+
const token = await testutils.getUserWithPermissions(api, 'testdoc: create')
|
|
15
15
|
await request(app)
|
|
16
16
|
.post('/testdoc')
|
|
17
17
|
.set('x-access-token', token)
|
|
@@ -28,7 +28,7 @@ describe('querying collections', function () {
|
|
|
28
28
|
})
|
|
29
29
|
|
|
30
30
|
it('create doc 2', async function () {
|
|
31
|
-
const token = await
|
|
31
|
+
const token = await testutils.getUserWithPermissions(api, 'testdoc: create')
|
|
32
32
|
await request(app)
|
|
33
33
|
.post('/testdoc')
|
|
34
34
|
.set('x-access-token', token)
|
|
@@ -42,7 +42,7 @@ describe('querying collections', function () {
|
|
|
42
42
|
})
|
|
43
43
|
|
|
44
44
|
it('create doc 3', async function () {
|
|
45
|
-
const token = await
|
|
45
|
+
const token = await testutils.getUserWithPermissions(api, 'testdoc: create')
|
|
46
46
|
await request(app)
|
|
47
47
|
.post('/testdoc')
|
|
48
48
|
.set('x-access-token', token)
|
|
@@ -60,7 +60,7 @@ describe('querying collections', function () {
|
|
|
60
60
|
let token
|
|
61
61
|
|
|
62
62
|
it('read doc by id', async function () {
|
|
63
|
-
token = await
|
|
63
|
+
token = await testutils.getUserWithPermissions(api, 'testdoc: view')
|
|
64
64
|
const res = await request(app)
|
|
65
65
|
.get('/testdoc/testid123')
|
|
66
66
|
.set('x-access-token', token)
|
|
@@ -202,7 +202,7 @@ describe('querying collections', function () {
|
|
|
202
202
|
})
|
|
203
203
|
|
|
204
204
|
it('sort by deep field ascending', async function () {
|
|
205
|
-
const token = await
|
|
205
|
+
const token = await testutils.getUserWithPermissions(api, 'testdoc: view')
|
|
206
206
|
const res = await request(app)
|
|
207
207
|
.get('/testdoc?orderby={"data.number":1}')
|
|
208
208
|
.set('x-access-token', token)
|
|
@@ -214,7 +214,7 @@ describe('querying collections', function () {
|
|
|
214
214
|
})
|
|
215
215
|
|
|
216
216
|
it('sort by deep field descending', async function () {
|
|
217
|
-
const token = await
|
|
217
|
+
const token = await testutils.getUserWithPermissions(api, 'testdoc: view')
|
|
218
218
|
const res = await request(app)
|
|
219
219
|
.get('/testdoc?orderby={"meta.created":-1}')
|
|
220
220
|
.set('x-access-token', token)
|
|
@@ -226,7 +226,7 @@ describe('querying collections', function () {
|
|
|
226
226
|
})
|
|
227
227
|
|
|
228
228
|
it('project a specific field', async function () {
|
|
229
|
-
const token = await
|
|
229
|
+
const token = await testutils.getUserWithPermissions(api, 'testdoc: view')
|
|
230
230
|
const res = await request(app)
|
|
231
231
|
.get('/testdoc?fields={"title":1}')
|
|
232
232
|
.set('x-access-token', token)
|
|
@@ -237,7 +237,7 @@ describe('querying collections', function () {
|
|
|
237
237
|
})
|
|
238
238
|
|
|
239
239
|
it('project a specific field on get', async function () {
|
|
240
|
-
const token = await
|
|
240
|
+
const token = await testutils.getUserWithPermissions(api, 'testdoc: view')
|
|
241
241
|
const res = await request(app)
|
|
242
242
|
.get('/testdoc/test1?fields={"title":1}')
|
|
243
243
|
.set('x-access-token', token)
|
|
@@ -247,7 +247,7 @@ describe('querying collections', function () {
|
|
|
247
247
|
})
|
|
248
248
|
|
|
249
249
|
it('project deep fields', async function() {
|
|
250
|
-
const token = await
|
|
250
|
+
const token = await testutils.getUserWithPermissions(api, 'testdoc: view')
|
|
251
251
|
const res = await request(app)
|
|
252
252
|
.get('/testdoc?fields={"data":1}')
|
|
253
253
|
.set('x-access-token', token)
|
|
@@ -266,4 +266,4 @@ describe('querying collections', function () {
|
|
|
266
266
|
expect(res2.body[0].data.number).to.equal(0)
|
|
267
267
|
expect(res2.body[0].data.field).to.be.undefined
|
|
268
268
|
})
|
|
269
|
-
})
|
|
269
|
+
})
|
package/test/test.js
CHANGED
|
@@ -70,7 +70,7 @@ describe('General Tests:', () => {
|
|
|
70
70
|
res.send(req.user)
|
|
71
71
|
})
|
|
72
72
|
|
|
73
|
-
const token = await
|
|
73
|
+
const token = await testutils.getUserWithPermissions(api, [])
|
|
74
74
|
const res = await request(app)
|
|
75
75
|
.get('/test')
|
|
76
76
|
.set('x-access-token', token)
|