Skip to content

Commit 7f9ecb6

Browse files
better use of the new xss hook plus two new organization hooks
1 parent b649f11 commit 7f9ecb6

File tree

6 files changed

+128
-31
lines changed

6 files changed

+128
-31
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const { getByDot, deleteByDot } = require('feathers-hooks-common');
2+
3+
module.exports = function restrictReviewAndEnableChange () { // eslint-disable-line no-unused-vars
4+
return (hook) => {
5+
6+
if (!getByDot(hook, 'params.before')) {
7+
throw new Error('The "restrictReviewAndEnableChange" hook should be used after the "stashBefore()" hook');
8+
}
9+
10+
const role = getByDot(hook, 'params.user.role');
11+
const isModOrAdmin = role && ['admin', 'moderator'].includes(role);
12+
const isReviewed = getByDot(hook, 'params.before.isReviewed');
13+
const userId = getByDot(hook, 'params.user._id');
14+
const isOwner = userId && getByDot(hook, 'params.before.userId');
15+
16+
// only allow mods and admins to change the review status
17+
if (!isModOrAdmin) {
18+
deleteByDot(hook.data, 'isReviewed');
19+
}
20+
21+
// only allow changes to mods, admin and owners (if its already reviewed)
22+
if (!isModOrAdmin && (!isOwner || (isOwner && !isReviewed))) {
23+
deleteByDot(hook.data, 'isEnabled');
24+
}
25+
26+
return hook;
27+
};
28+
};
29+
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
const { getByDot } = require('feathers-hooks-common');
2+
const errors = require('feathers-errors');
3+
4+
module.exports = function restrictToOwnerOrModerator (query = {}) { // eslint-disable-line no-unused-vars
5+
return function (hook) {
6+
if (hook.type !== 'before') {
7+
throw new Error('The "restrictToOwnerOrModerator" hook should only be used as a "before" hook.');
8+
}
9+
const isFindOrGet = ['find', 'get'].includes(hook.method);
10+
if (!isFindOrGet && !getByDot(hook, 'params.before')) {
11+
throw new Error('The "restrictToOwnerOrModerator" hook should be used after the "stashBefore()" hook');
12+
}
13+
14+
if (!hook.params || !hook.params.user) {
15+
return false;
16+
}
17+
18+
const role = getByDot(hook, 'params.user.role');
19+
const isModOrAdmin = role && ['admin', 'moderator'].includes(role);
20+
21+
const userId = getByDot(hook, 'params.user._id');
22+
const isOwner = userId && getByDot(hook, 'params.before.userId');
23+
24+
// allow for mods or admins
25+
if (isModOrAdmin) {
26+
return hook;
27+
}
28+
29+
// change the query if the method is find or get
30+
if (isFindOrGet) {
31+
// add given query on top of it
32+
hook.data = Object.assign(hook.data, query);
33+
// if not an mod or admin, restrict to owner
34+
hook.data.userId = userId;
35+
36+
return hook;
37+
}
38+
39+
let hasError = false;
40+
const keys = Object.keys(query);
41+
if (!isOwner && keys.length) {
42+
keys.forEach((key) => {
43+
if (query[key] !== getByDot(hook, `params.before.${key}`)) {
44+
hasError = true;
45+
}
46+
});
47+
if (hasError) {
48+
// if any of the given query params is not identical with the current values this action is forbidden
49+
throw new errors.Forbidden('You can\'t alter this record!');
50+
}
51+
} else if (!isOwner) {
52+
// his action is forbidden if its not the owner
53+
throw new errors.Forbidden('You can\'t alter this record!');
54+
}
55+
56+
return hook;
57+
};
58+
};
59+

server/models/organizations.model.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ module.exports = function (app) {
3030
type: Boolean,
3131
default: false
3232
},
33+
isReviewed: {
34+
type: Boolean,
35+
default: false
36+
},
3337
deleted: {
3438
type: Boolean,
3539
default: false

server/seeder/development/organizations.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ module.exports = (seederstore) => {
2121
isVerified: () => seedHelpers.randomItem([true, false]),
2222
deletedAt: null,
2323
isEnabled: true,
24+
isReviewed: true,
2425
createdAt: '{{date.recent}}',
2526
updatedAt: '{{date.recent}}',
2627
wasSeeded: true

server/services/contributions/contributions.hooks.js

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,13 @@ const thumbs = {
7979
}
8080
};
8181

82+
const xssFields = ['content', 'contentExcerpt', 'cando.reason'];
83+
8284
module.exports = {
8385
before: {
84-
all: [],
86+
all: [
87+
xss({ fields: xssFields })
88+
],
8589
find: [
8690
unless(isModerator(),
8791
excludeDisabled()
@@ -103,7 +107,6 @@ module.exports = {
103107
isVerified()
104108
),
105109
associateCurrentUser(),
106-
xss({ fields: ['content', 'contentExcerpt'] }),
107110
createSlug({ field: 'title' }),
108111
saveRemoteImages(['teaserImg']),
109112
createExcerpt()
@@ -117,7 +120,6 @@ module.exports = {
117120
excludeDisabled(),
118121
restrictToOwner()
119122
),
120-
xss({ fields: ['content', 'contentExcerpt'] }),
121123
saveRemoteImages(['teaserImg']),
122124
createExcerpt()
123125
],
@@ -130,7 +132,6 @@ module.exports = {
130132
excludeDisabled(),
131133
restrictToOwner()
132134
),
133-
xss({ fields: ['content', 'contentExcerpt'] }),
134135
saveRemoteImages(['teaserImg']),
135136
createExcerpt()
136137
],
@@ -146,6 +147,7 @@ module.exports = {
146147

147148
after: {
148149
all: [
150+
xss({ fields: xssFields }),
149151
populate({ schema: userSchema }),
150152
populate({ schema: categoriesSchema }),
151153
populate({ schema: candosSchema }),
@@ -155,27 +157,22 @@ module.exports = {
155157
when(isSingleItem(),
156158
getAssociatedCanDos()
157159
),
158-
xss({ fields: ['content', 'contentExcerpt'] }),
159160
thumbnails(thumbs)
160161
],
161162
get: [
162163
getAssociatedCanDos(),
163-
xss({ fields: ['content', 'contentExcerpt'] }),
164164
thumbnails(thumbs)
165165
],
166166
create: [
167167
createMentionNotifications(),
168-
xss({ fields: ['content', 'contentExcerpt'] }),
169168
thumbnails(thumbs)
170169
],
171170
update: [
172171
createMentionNotifications(),
173-
xss({ fields: ['content', 'contentExcerpt'] }),
174172
thumbnails(thumbs)
175173
],
176174
patch: [
177175
createMentionNotifications(),
178-
xss({ fields: ['content', 'contentExcerpt'] }),
179176
thumbnails(thumbs)
180177
],
181178
remove: []

server/services/organizations/organizations.hooks.js

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
const { unless, isProvider, softDelete } = require('feathers-hooks-common');
1+
const { unless, when, isProvider, softDelete, stashBefore } = require('feathers-hooks-common');
22
const { isVerified } = require('feathers-authentication-management').hooks;
33
const { authenticate } = require('feathers-authentication').hooks;
4-
const { associateCurrentUser, restrictToOwner } = require('feathers-authentication-hooks');
4+
const { associateCurrentUser } = require('feathers-authentication-hooks');
55
const createSlug = require('../../hooks/create-slug');
66
const saveRemoteImages = require('../../hooks/save-remote-images');
77
const createExcerpt = require('../../hooks/create-excerpt');
88
const isModerator = require('../../hooks/is-moderator-boolean');
9-
const excludeDisabled = require('../../hooks/exclude-disabled');
9+
// const excludeDisabled = require('../../hooks/exclude-disabled');
1010
const thumbnails = require('../../hooks/thumbnails');
11+
const restrictToOwnerOrModerator = require('../../hooks/restrictToOwnerOrModerator');
12+
const restrictReviewAndEnableChange = require('../../hooks/restrictReviewAndEnableChange');
13+
const xss = require('../../hooks/xss');
1114

1215
const thumbnailOptions = {
1316
logo: {
@@ -22,25 +25,32 @@ const thumbnailOptions = {
2225
}
2326
};
2427

28+
const xssFields = ['description', 'descriptionExcerpt'];
29+
2530
module.exports = {
2631
before: {
27-
all: softDelete(),
32+
all: [
33+
softDelete(),
34+
xss({ fields: xssFields })
35+
],
2836
find: [
29-
unless(isModerator(),
30-
excludeDisabled()
31-
)
37+
restrictToOwnerOrModerator({ isEnabled: true, isReviewed: true })
3238
],
3339
get: [
34-
unless(isModerator(),
35-
excludeDisabled()
36-
)
40+
restrictToOwnerOrModerator({ isEnabled: true, isReviewed: true })
3741
],
3842
create: [
3943
authenticate('jwt'),
4044
// Allow seeder to seed contributions
4145
unless(isProvider('server'),
4246
isVerified()
4347
),
48+
when(isModerator(),
49+
hook => {
50+
hook.data.isReviewed = true;
51+
return hook;
52+
}
53+
),
4454
associateCurrentUser(),
4555
createSlug({ field: 'name' }),
4656
createExcerpt({ field: 'description' }),
@@ -51,10 +61,9 @@ module.exports = {
5161
unless(isProvider('server'),
5262
isVerified()
5363
),
54-
unless(isModerator(),
55-
excludeDisabled(),
56-
restrictToOwner()
57-
),
64+
stashBefore(),
65+
restrictReviewAndEnableChange(),
66+
restrictToOwnerOrModerator({ isEnabled: true }),
5867
createSlug({ field: 'name', overwrite: true }),
5968
createExcerpt({ field: 'description' }),
6069
saveRemoteImages(['logo', 'coverImg'])
@@ -64,26 +73,24 @@ module.exports = {
6473
unless(isProvider('server'),
6574
isVerified()
6675
),
67-
unless(isModerator(),
68-
excludeDisabled(),
69-
restrictToOwner()
70-
),
76+
stashBefore(),
77+
restrictReviewAndEnableChange(),
78+
restrictToOwnerOrModerator({ isEnabled: true }),
7179
createSlug({ field: 'name', overwrite: true }),
7280
createExcerpt({ field: 'description' }),
7381
saveRemoteImages(['logo', 'coverImg'])
7482
],
7583
remove: [
7684
authenticate('jwt'),
7785
isVerified(),
78-
unless(isModerator(),
79-
excludeDisabled(),
80-
restrictToOwner()
81-
)
86+
stashBefore(),
87+
restrictToOwnerOrModerator({ isEnabled: true })
8288
]
8389
},
8490

8591
after: {
8692
all: [
93+
xss({ fields: xssFields })
8794
// populate({ schema: userSchema }),
8895
// populate({ schema: followerSchema })
8996
],

0 commit comments

Comments
 (0)