Skip to content

Commit a91071f

Browse files
committed
allowing multiple auth levels
1 parent f86a3bc commit a91071f

File tree

3 files changed

+179
-130
lines changed

3 files changed

+179
-130
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ In the example above we extended the **userSchema** by adding a *permissions* ob
8989
The permissions object consists or properties that represent your authorization levels (or groups). For each group, there are 4 permissions you can configure.
9090
* `save` (create) - Boolean
9191
* `remove` - Boolean
92-
* `update` - [array of fields] *NOTE: if `upsert: true`, the group will need to have `save` permissions too*
92+
* `write` - [array of fields] *NOTE: if `upsert: true`, the group will need to have `save` permissions too*
9393
* `read` (find) - [array of fields]
9494

9595
You can also specify a `defaults` group, which represents permissions that are available to all groups.
@@ -100,6 +100,8 @@ You can also specify a `defaults` group, which represents permissions that are a
100100

101101
***example update***
102102

103+
You can also specify an array of authentication levels. This would merge the settings of each auth level.
104+
103105
```
104106
users.update({user_id: userUpdate.user_id}, userUpdate, {
105107
authLevel: 'admin'

index.js

Lines changed: 175 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -2,140 +2,187 @@
22

33
var _ = require('lodash');
44

5-
module.exports = function(schema) {
6-
schema.pre('save', function(next) {
7-
save(this, next);
8-
});
9-
schema.pre('findOneAndRemove', function(next) {
10-
remove(this, next);
11-
});
12-
schema.pre('find', function(next) {
13-
find(this, next);
14-
});
15-
schema.pre('findOne', function(next) {
16-
find(this, next);
17-
});
18-
schema.pre('update', function(next) {
19-
update(this, next);
20-
});
21-
schema.pre('findOneAndUpdate', function(next) {
22-
update(this, next);
23-
});
24-
25-
function save(schema, next) {
26-
var vm = schema;
27-
if (vm.options && vm.options.authLevel) {
28-
if (vm.schema.permissions[vm.options.authLevel] && vm.schema.permissions[vm.options.authLevel].save) {
29-
//check to see if the group has permission to save a new document
30-
return next();
31-
} else {
32-
return next({message: 'permission denied', reason: 'you do not have access to the following permissions: [save]'});
33-
}
34-
} else {
35-
return next();
5+
module.exports = function (schema) {
6+
schema.pre('save', function (next) {
7+
save(this, next);
8+
});
9+
schema.pre('findOneAndRemove', function (next) {
10+
remove(this, next);
11+
});
12+
schema.pre('find', function (next) {
13+
find(this, next);
14+
});
15+
schema.pre('findOne', function (next) {
16+
find(this, next);
17+
});
18+
schema.pre('update', function (next) {
19+
update(this, next);
20+
});
21+
schema.pre('findOneAndUpdate', function (next) {
22+
update(this, next);
23+
});
24+
25+
function hasPermission(vm, action) {
26+
if (!vm.schema.permissions[vm.options.authLevel]) {
27+
return false;
28+
}
29+
var authLevel = vm.options.authLevel;
30+
31+
if (Array.isArray(authLevel)) {
32+
return authLevel.filter(function (level) {
33+
return !!vm.schema.permissions[level][action];
34+
}).length > 0;
35+
} else {
36+
return vm.schema.permissions[authLevel][action];
37+
}
3638
}
37-
}
38-
39-
function remove(schema, next) {
40-
var vm = schema;
41-
if (vm.options && vm.options.authLevel) {
42-
if (vm.schema.permissions[vm.options.authLevel] && vm.schema.permissions[vm.options.authLevel].remove) {
43-
//check to see if the group has permission to remove a document
44-
return next();
45-
} else {
46-
return next({message: 'permission denied', reason: 'you do not have access to the following permissions: [remove]'});
47-
}
48-
} else {
49-
return next();
39+
40+
function authOptionPresent(vm) {
41+
return vm.options && vm.options.authLevel;
5042
}
51-
}
52-
53-
function find(schema, next) {
54-
var vm = schema;
55-
var authorizedFields = [];
56-
if (vm.options && vm.options.authLevel) {
57-
if (vm.schema.permissions[vm.options.authLevel] && vm.schema.permissions[vm.options.authLevel].read) {
58-
//check to see if the group has any read permissions and add to the authorizedFields array
59-
authorizedFields = authorizedFields.concat(vm.schema.permissions[vm.options.authLevel].read);
60-
}
61-
if (vm.schema.permissions.defaults && vm.schema.permissions.defaults.read) {
62-
//check to see if there are any default read permissions and add to the authorizedFields array
63-
authorizedFields = authorizedFields.concat(vm.schema.permissions.defaults.read);
64-
}
65-
66-
//create a projection object for mongoose based on the authorizedFields array
67-
var sanitizedFind = {};
68-
authorizedFields.forEach(function(field) {
69-
sanitizedFind[field] = 1;
70-
});
71-
72-
//Check to see if group has the permission to permorm a find using the specified fields
73-
var discrepancies = _.difference(Object.keys(vm._conditions), Object.keys(sanitizedFind));
74-
if (discrepancies[0]) {
75-
//if a group is searching by a field they do not have access to, return an error
76-
return next({message: 'permission denied', reason: 'you do not have access to the following fields: [' + discrepancies.toString() + ']'});
77-
} else {
78-
vm._fields = sanitizedFind;
79-
return next();
80-
}
81-
} else {
82-
return next();
43+
44+
function getAuthorizedFields(vm, action) {
45+
var authLevel = vm.options.authLevel;
46+
47+
if (Array.isArray(authLevel)) {
48+
return authLevel.reduce(function (acc, level) {
49+
var fields = vm.schema.permissions[level][action] || [];
50+
return acc.concat(fields)
51+
}, []);
52+
} else {
53+
return vm.schema.permissions[authLevel][action] || [];
54+
}
8355
}
84-
}
85-
86-
function update(schema, next) {
87-
var vm = schema;
88-
var authorizedFields = [];
89-
var authorizedReturnFields = [];
90-
if (vm.options && vm.options.authLevel) {
91-
if (vm.options.upsert && !vm.schema.permissions[vm.options.authLevel].save) {
92-
//check to see if 'upsert: true' option is set, then verify if group has save permission
93-
return next({message: 'permission denied', reason: 'you do not have access to the following permissions: [save]'});
94-
}
95-
if (vm.schema.permissions[vm.options.authLevel] && vm.schema.permissions[vm.options.authLevel].write) {
96-
//check to see if group has any write permissions and add to the authorizedFields array
97-
authorizedFields = authorizedFields.concat(vm.schema.permissions[vm.options.authLevel].write);
98-
}
99-
if (vm.schema.permissions.defaults && vm.schema.permissions.defaults.write) {
100-
//check to see if there are any default write permissions and add to the authorizedFields array
101-
authorizedFields = authorizedFields.concat(vm.schema.permissions.defaults.write);
102-
}
103-
104-
//create an update object that has been sanitized based on permissions
105-
var sanitizedUpdate = {};
106-
authorizedFields.forEach(function(field) {
107-
sanitizedUpdate[field] = vm._update[field];
108-
});
109-
110-
//check to see if the group is trying to update a field it does not have permission to
111-
var discrepancies = _.difference(Object.keys(vm._update), Object.keys(sanitizedUpdate));
112-
if (discrepancies[0]) {
113-
//if a group is searching by a field they do not have access to, return an error
114-
return next({message: 'permission denied', reason: 'you do not have access to the following fields: [' + discrepancies.toString() + ']'});
115-
} else {
116-
117-
//Detect which fields can be returned if 'new: true' is set
118-
if (vm.schema.permissions[vm.options.authLevel] && vm.schema.permissions[vm.options.authLevel].read) {
119-
120-
//check to see if the group has any read permissions and add to the authorizedFields array
121-
authorizedReturnFields = authorizedReturnFields.concat(vm.schema.permissions[vm.options.authLevel].read);
56+
57+
function save(schema, next) {
58+
var vm = schema;
59+
if (authOptionPresent(vm)) {
60+
if (hasPermission(vm, 'save')) {
61+
//check to see if the group has permission to save a new document
62+
return next();
63+
} else {
64+
return next({
65+
message: 'permission denied',
66+
reason: 'you do not have access to the following permissions: [save]'
67+
});
68+
}
69+
} else {
70+
return next();
12271
}
123-
if (vm.schema.permissions.defaults && vm.schema.permissions.defaults.read) {
72+
}
12473

125-
//check to see if there are any default read permissions and add to the authorizedFields array
126-
authorizedReturnFields = authorizedReturnFields.concat(vm.schema.permissions.defaults.read);
74+
function remove(schema, next) {
75+
var vm = schema;
76+
if (authOptionPresent(vm)) {
77+
if (hasPermission(vm, 'remove')) {
78+
//check to see if the group has permission to remove a document
79+
return next();
80+
} else {
81+
return next({
82+
message: 'permission denied',
83+
reason: 'you do not have access to the following permissions: [remove]'
84+
});
85+
}
86+
} else {
87+
return next();
12788
}
89+
}
90+
91+
function find(schema, next) {
92+
var vm = schema;
93+
var authorizedFields = [];
94+
if (authOptionPresent(vm)) {
95+
if (vm.schema.permissions[vm.options.authLevel] && vm.schema.permissions[vm.options.authLevel].read) {
96+
//check to see if the group has any read permissions and add to the authorizedFields array
97+
authorizedFields = authorizedFields.concat(getAuthorizedFields(vm, 'read'));
98+
}
99+
if (vm.schema.permissions.defaults && vm.schema.permissions.defaults.read) {
100+
//check to see if there are any default read permissions and add to the authorizedFields array
101+
authorizedFields = authorizedFields.concat(vm.schema.permissions.defaults.read);
102+
}
128103

129-
//create a sanitizedReturnFields object that will be used to return only the fields that a group has access to read
130-
var sanitizedReturnFields = {};
131-
authorizedReturnFields.forEach(function(field) {
132-
sanitizedReturnFields[field] = 1;
133-
});
134-
vm._fields = sanitizedReturnFields;
135-
return next();
136-
}
137-
} else {
138-
return next();
104+
//create a projection object for mongoose based on the authorizedFields array
105+
var sanitizedFind = {};
106+
authorizedFields.forEach(function (field) {
107+
sanitizedFind[field] = 1;
108+
});
109+
110+
//Check to see if group has the permission to perform a find using the specified fields
111+
var discrepancies = _.difference(Object.keys(vm._conditions), Object.keys(sanitizedFind));
112+
if (discrepancies[0]) {
113+
//if a group is searching by a field they do not have access to, return an error
114+
return next({
115+
message: 'permission denied',
116+
reason: 'you do not have access to the following fields: [' + discrepancies.toString() + ']'
117+
});
118+
} else {
119+
vm._fields = sanitizedFind;
120+
return next();
121+
}
122+
} else {
123+
return next();
124+
}
125+
}
126+
127+
function update(schema, next) {
128+
var vm = schema;
129+
var authorizedFields = [];
130+
var authorizedReturnFields = [];
131+
if (authOptionPresent(vm)) {
132+
if (vm.options.upsert && !hasPermission(vm, 'save')) {
133+
//check to see if 'upsert: true' option is set, then verify if group has save permission
134+
return next({
135+
message: 'permission denied',
136+
reason: 'you do not have access to the following permissions: [save]'
137+
});
138+
}
139+
if (vm.schema.permissions[vm.options.authLevel] && vm.schema.permissions[vm.options.authLevel].write) {
140+
//check to see if group has any write permissions and add to the authorizedFields array
141+
authorizedFields = authorizedFields.concat(getAuthorizedFields(vm, 'write'));
142+
}
143+
if (vm.schema.permissions.defaults && vm.schema.permissions.defaults.write) {
144+
//check to see if there are any default write permissions and add to the authorizedFields array
145+
authorizedFields = authorizedFields.concat(vm.schema.permissions.defaults.write);
146+
}
147+
148+
//create an update object that has been sanitized based on permissions
149+
var sanitizedUpdate = {};
150+
authorizedFields.forEach(function (field) {
151+
sanitizedUpdate[field] = vm._update[field];
152+
});
153+
154+
//check to see if the group is trying to update a field it does not have permission to
155+
var discrepancies = _.difference(Object.keys(vm._update), Object.keys(sanitizedUpdate));
156+
if (discrepancies[0]) {
157+
//if a group is searching by a field they do not have access to, return an error
158+
return next({
159+
message: 'permission denied',
160+
reason: 'you do not have access to the following fields: [' + discrepancies.toString() + ']'
161+
});
162+
} else {
163+
164+
//Detect which fields can be returned if 'new: true' is set
165+
if (vm.schema.permissions[vm.options.authLevel] && vm.schema.permissions[vm.options.authLevel].read) {
166+
167+
//check to see if the group has any read permissions and add to the authorizedFields array
168+
authorizedReturnFields = authorizedReturnFields.concat(getAuthorizedFields(vm, 'read'));
169+
}
170+
if (vm.schema.permissions.defaults && vm.schema.permissions.defaults.read) {
171+
172+
//check to see if there are any default read permissions and add to the authorizedFields array
173+
authorizedReturnFields = authorizedReturnFields.concat(vm.schema.permissions.defaults.read);
174+
}
175+
176+
//create a sanitizedReturnFields object that will be used to return only the fields that a group has access to read
177+
var sanitizedReturnFields = {};
178+
authorizedReturnFields.forEach(function (field) {
179+
sanitizedReturnFields[field] = 1;
180+
});
181+
vm._fields = sanitizedReturnFields;
182+
return next();
183+
}
184+
} else {
185+
return next();
186+
}
139187
}
140-
}
141188
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mongoose-authorization",
3-
"version": "0.0.7",
3+
"version": "0.1.0",
44
"description": "Data level authorization for Mongoose",
55
"main": "index.js",
66
"scripts": {

0 commit comments

Comments
 (0)