Skip to content

Commit 8cb26a8

Browse files
authored
Upgrade permissions (#22)
* Making the permissions key unwritable We'll be able to trust it more that way * Document.save and Document.remove now check for embedded permissions first
1 parent d1b2c49 commit 8cb26a8

6 files changed

Lines changed: 49 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# 2.0.0
22

33
- Removing the ability to call Model.remove() and Model.create() since those aren't compatible with how this library works.
4-
-
4+
- Muuuuch better tests
5+
- Embedded permissions object cannot be overwritten
6+
- When a document has embedded permissions, those permissions will be checks when a save or remove is being done. That way someone cannot write to an object in a way that changes their permssions and then try to save it.

__tests__/embedPermissions.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,10 @@ test('Verify that the permissions data cannot be changed', (t) => {
121121
Error,
122122
'The permissions object shouldn\'t be writable [remove]',
123123
);
124+
125+
t.throws(
126+
() => { doc.permissions = {}; },
127+
Error,
128+
'The permissions field should not be writable',
129+
);
124130
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const test = require('ava');
2+
3+
test.todo('Write tests for getEmbeddedPermissions');

index.js

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,25 @@ const authIsDisabled = require('./src/authIsDisabled');
55
const sanitizeDocumentList = require('./src/sanitizeDocumentList');
66
const getUpdatePaths = require('./src/getUpdatePaths');
77
const resolveAuthLevel = require('./src/resolveAuthLevel');
8+
const getEmbeddedPermission = require('./src/getEmbeddedPermission');
89
const PermissionDeniedError = require('./src/PermissionDeniedError');
910
const IncompatibleMethodError = require('./src/IncompatibleMethodError');
1011

1112
module.exports = (schema, installationOptions) => {
1213
async function save(doc, options) {
13-
const authLevels = await resolveAuthLevel(schema, options, doc);
14-
if (doc.isNew && !hasPermission(schema, authLevels, 'create')) {
14+
let authorizedFields = getEmbeddedPermission(doc, options, 'write');
15+
let canCreate = false;
16+
17+
if (authorizedFields === undefined) {
18+
const authLevels = await resolveAuthLevel(schema, options, doc);
19+
authorizedFields = getAuthorizedFields(schema, authLevels, 'write');
20+
canCreate = hasPermission(schema, authLevels, 'create');
21+
}
22+
23+
if (doc.isNew && !canCreate) {
1524
throw new PermissionDeniedError('create');
1625
}
1726

18-
const authorizedFields = getAuthorizedFields(schema, authLevels, 'write');
1927
const modifiedPaths = doc.modifiedPaths();
2028
const discrepancies = _.difference(modifiedPaths, authorizedFields);
2129

@@ -32,8 +40,14 @@ module.exports = (schema, installationOptions) => {
3240
}
3341

3442
async function removeDoc(doc, options) {
35-
const authLevels = await resolveAuthLevel(schema, options, doc);
36-
if (!hasPermission(schema, authLevels, 'remove')) {
43+
let canRemove = getEmbeddedPermission(doc, options, 'remove');
44+
45+
if (canRemove === undefined) {
46+
const authLevels = await resolveAuthLevel(schema, options, doc);
47+
canRemove = hasPermission(schema, authLevels, 'remove');
48+
}
49+
50+
if (!canRemove) {
3751
throw new PermissionDeniedError('remove');
3852
}
3953
}

src/embedPermissions.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ function embedPermissions(schema, options, authLevels, doc) {
1212
throw new Error(`Cannot embed permissions into mongoose document at \`${permsKey}\`because the key is already present in the document. Please specify a custom key.`);
1313
}
1414

15-
doc[permsKey] = {
16-
read: getAuthorizedFields(schema, authLevels, 'read'),
17-
write: getAuthorizedFields(schema, authLevels, 'write'),
18-
remove: hasPermission(schema, authLevels, 'remove'),
19-
};
15+
Object.defineProperty(doc, permsKey, {
16+
value: {
17+
read: getAuthorizedFields(schema, authLevels, 'read'),
18+
write: getAuthorizedFields(schema, authLevels, 'write'),
19+
remove: hasPermission(schema, authLevels, 'remove'),
20+
},
21+
writable: false,
22+
});
2023

2124
// Freeze the object so this data can't be altered (even accidentally)
2225
Object.freeze(doc[permsKey]);

src/getEmbeddedPermission.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const _ = require('lodash');
2+
3+
module.exports = function getEmbeddedPermission(doc, options, action) {
4+
let permsKey = 'permissions';
5+
if (options.permissions && options.permissions !== true) {
6+
permsKey = options.permissions;
7+
}
8+
9+
return _.get(doc, `${permsKey}.${action}`);
10+
};

0 commit comments

Comments
 (0)