@strapi/plugin-users-permissions 0.0.0-next.f45143c5e2a8a9d85691d0abf79a3f42024a0c71 → 0.0.0-next.f4ff842a3cb7b83db540bee67554b704e042b042
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/.eslintignore +1 -2
- package/.eslintrc +17 -0
- package/admin/src/components/BoundRoute/{index.js → index.jsx} +5 -3
- package/admin/src/components/FormModal/Input/{index.js → index.jsx} +6 -3
- package/admin/src/components/FormModal/{index.js → index.jsx} +13 -10
- package/admin/src/components/Permissions/PermissionRow/{SubCategory.js → SubCategory.jsx} +26 -5
- package/admin/src/components/Permissions/PermissionRow/{index.js → index.jsx} +4 -2
- package/admin/src/components/Permissions/{index.js → index.jsx} +6 -5
- package/admin/src/components/Policies/{index.js → index.jsx} +3 -2
- package/admin/src/components/UsersPermissions/{index.js → index.jsx} +8 -5
- package/admin/src/{permissions.js → constants.js} +1 -3
- package/admin/src/contexts/UsersPermissionsContext/{index.js → index.jsx} +1 -0
- package/admin/src/index.js +19 -28
- package/admin/src/pages/AdvancedSettings/{index.js → index.jsx} +68 -52
- package/admin/src/pages/AdvancedSettings/utils/schema.js +1 -1
- package/admin/src/pages/EmailTemplates/components/{EmailForm.js → EmailForm.jsx} +14 -13
- package/admin/src/pages/EmailTemplates/components/{EmailTable.js → EmailTable.jsx} +9 -7
- package/admin/src/pages/EmailTemplates/{index.js → index.jsx} +77 -63
- package/admin/src/pages/EmailTemplates/utils/schema.js +1 -1
- package/admin/src/pages/Providers/{index.js → index.jsx} +91 -86
- package/admin/src/pages/Providers/utils/forms.js +1 -1
- package/admin/src/pages/Roles/{CreatePage/utils/schema.js → constants.js} +2 -4
- package/admin/src/pages/Roles/hooks/usePlugins.js +78 -0
- package/admin/src/pages/Roles/index.jsx +33 -0
- package/admin/src/pages/Roles/pages/CreatePage.jsx +199 -0
- package/admin/src/pages/Roles/pages/EditPage.jsx +220 -0
- package/admin/src/pages/Roles/{ListPage/components/TableBody.js → pages/ListPage/components/TableBody.jsx} +46 -15
- package/admin/src/pages/Roles/{ListPage/index.js → pages/ListPage/index.jsx} +48 -47
- package/admin/src/pages/Roles/{ListPage → pages/ListPage}/utils/api.js +3 -4
- package/admin/src/translations/ru.json +50 -26
- package/admin/src/translations/zh-Hans.json +80 -80
- package/admin/src/utils/index.js +1 -2
- package/dist/_chunks/ar-MvD8Ghac.mjs +44 -0
- package/dist/_chunks/ar-MvD8Ghac.mjs.map +1 -0
- package/dist/_chunks/ar-t5qTFaAD.js +44 -0
- package/dist/_chunks/ar-t5qTFaAD.js.map +1 -0
- package/dist/_chunks/cs-BMuXwxA1.mjs +50 -0
- package/dist/_chunks/cs-BMuXwxA1.mjs.map +1 -0
- package/dist/_chunks/cs-I8N4u-Sd.js +50 -0
- package/dist/_chunks/cs-I8N4u-Sd.js.map +1 -0
- package/dist/_chunks/de-YTjtq89K.js +62 -0
- package/dist/_chunks/de-YTjtq89K.js.map +1 -0
- package/dist/_chunks/de-zs2qqc0W.mjs +62 -0
- package/dist/_chunks/de-zs2qqc0W.mjs.map +1 -0
- package/dist/_chunks/dk-HctVBMsG.mjs +86 -0
- package/dist/_chunks/dk-HctVBMsG.mjs.map +1 -0
- package/dist/_chunks/dk-TF-dWjzl.js +86 -0
- package/dist/_chunks/dk-TF-dWjzl.js.map +1 -0
- package/dist/_chunks/en-CE3wEy_c.mjs +86 -0
- package/dist/_chunks/en-CE3wEy_c.mjs.map +1 -0
- package/dist/_chunks/en-m608rMZx.js +86 -0
- package/dist/_chunks/en-m608rMZx.js.map +1 -0
- package/dist/_chunks/es-9381tih_.mjs +86 -0
- package/dist/_chunks/es-9381tih_.mjs.map +1 -0
- package/dist/_chunks/es-XBQsB8_9.js +86 -0
- package/dist/_chunks/es-XBQsB8_9.js.map +1 -0
- package/dist/_chunks/fr-6cz3U-IF.js +50 -0
- package/dist/_chunks/fr-6cz3U-IF.js.map +1 -0
- package/dist/_chunks/fr-CMSc77If.mjs +50 -0
- package/dist/_chunks/fr-CMSc77If.mjs.map +1 -0
- package/dist/_chunks/id-RJ934rq-.js +62 -0
- package/dist/_chunks/id-RJ934rq-.js.map +1 -0
- package/dist/_chunks/id-SDuyIkZa.mjs +62 -0
- package/dist/_chunks/id-SDuyIkZa.mjs.map +1 -0
- package/dist/_chunks/index--ch5E4_K.js +638 -0
- package/dist/_chunks/index--ch5E4_K.js.map +1 -0
- package/dist/_chunks/index-MuQRbm-o.mjs +1159 -0
- package/dist/_chunks/index-MuQRbm-o.mjs.map +1 -0
- package/dist/_chunks/index-UrkRT2rC.js +320 -0
- package/dist/_chunks/index-UrkRT2rC.js.map +1 -0
- package/dist/_chunks/index-WCv8yGzw.js +1191 -0
- package/dist/_chunks/index-WCv8yGzw.js.map +1 -0
- package/dist/_chunks/index-eE2qjbfg.mjs +615 -0
- package/dist/_chunks/index-eE2qjbfg.mjs.map +1 -0
- package/dist/_chunks/index-iu-7wU8h.mjs +250 -0
- package/dist/_chunks/index-iu-7wU8h.mjs.map +1 -0
- package/dist/_chunks/index-mCVPA90k.mjs +385 -0
- package/dist/_chunks/index-mCVPA90k.mjs.map +1 -0
- package/dist/_chunks/index-mNv7pSw-.js +407 -0
- package/dist/_chunks/index-mNv7pSw-.js.map +1 -0
- package/dist/_chunks/index-rL_BoFHz.js +249 -0
- package/dist/_chunks/index-rL_BoFHz.js.map +1 -0
- package/dist/_chunks/index-xXp5S4OL.mjs +301 -0
- package/dist/_chunks/index-xXp5S4OL.mjs.map +1 -0
- package/dist/_chunks/it-YhZOlM2X.js +62 -0
- package/dist/_chunks/it-YhZOlM2X.js.map +1 -0
- package/dist/_chunks/it-bvH7DgQo.mjs +62 -0
- package/dist/_chunks/it-bvH7DgQo.mjs.map +1 -0
- package/dist/_chunks/ja-o_-JPvQv.mjs +48 -0
- package/dist/_chunks/ja-o_-JPvQv.mjs.map +1 -0
- package/dist/_chunks/ja-xssHUXFv.js +48 -0
- package/dist/_chunks/ja-xssHUXFv.js.map +1 -0
- package/dist/_chunks/ko-C3mHUSJa.js +86 -0
- package/dist/_chunks/ko-C3mHUSJa.js.map +1 -0
- package/dist/_chunks/ko-XJbPSez_.mjs +86 -0
- package/dist/_chunks/ko-XJbPSez_.mjs.map +1 -0
- package/dist/_chunks/ms-II5Ea73J.mjs +49 -0
- package/dist/_chunks/ms-II5Ea73J.mjs.map +1 -0
- package/dist/_chunks/ms-d0hfg65Z.js +49 -0
- package/dist/_chunks/ms-d0hfg65Z.js.map +1 -0
- package/dist/_chunks/nl-TA7TfK_5.js +48 -0
- package/dist/_chunks/nl-TA7TfK_5.js.map +1 -0
- package/dist/_chunks/nl-vEy6TN0K.mjs +48 -0
- package/dist/_chunks/nl-vEy6TN0K.mjs.map +1 -0
- package/dist/_chunks/pl-0pUL9hdA.js +86 -0
- package/dist/_chunks/pl-0pUL9hdA.js.map +1 -0
- package/dist/_chunks/pl-2VowaFGt.mjs +86 -0
- package/dist/_chunks/pl-2VowaFGt.mjs.map +1 -0
- package/dist/_chunks/pt-BR-WNOhafR4.js +44 -0
- package/dist/_chunks/pt-BR-WNOhafR4.js.map +1 -0
- package/dist/_chunks/pt-BR-sS1Xp3Jt.mjs +44 -0
- package/dist/_chunks/pt-BR-sS1Xp3Jt.mjs.map +1 -0
- package/dist/_chunks/pt-Rf9W51IO.mjs +48 -0
- package/dist/_chunks/pt-Rf9W51IO.mjs.map +1 -0
- package/dist/_chunks/pt-guNR9Gax.js +48 -0
- package/dist/_chunks/pt-guNR9Gax.js.map +1 -0
- package/dist/_chunks/ru-X3BMXDds.js +86 -0
- package/dist/_chunks/ru-X3BMXDds.js.map +1 -0
- package/dist/_chunks/ru-qKHnd5or.mjs +86 -0
- package/dist/_chunks/ru-qKHnd5or.mjs.map +1 -0
- package/dist/_chunks/sk-NWPw1oTN.js +50 -0
- package/dist/_chunks/sk-NWPw1oTN.js.map +1 -0
- package/dist/_chunks/sk-_Ryr-eTT.mjs +50 -0
- package/dist/_chunks/sk-_Ryr-eTT.mjs.map +1 -0
- package/dist/_chunks/sv-76NnbB__.js +86 -0
- package/dist/_chunks/sv-76NnbB__.js.map +1 -0
- package/dist/_chunks/sv-BqzScFXS.mjs +86 -0
- package/dist/_chunks/sv-BqzScFXS.mjs.map +1 -0
- package/dist/_chunks/th-WsknMEpq.mjs +60 -0
- package/dist/_chunks/th-WsknMEpq.mjs.map +1 -0
- package/dist/_chunks/th-cbppX21D.js +60 -0
- package/dist/_chunks/th-cbppX21D.js.map +1 -0
- package/dist/_chunks/tr-6mm_Fmz7.js +85 -0
- package/dist/_chunks/tr-6mm_Fmz7.js.map +1 -0
- package/dist/_chunks/tr-_DB1F1GW.mjs +85 -0
- package/dist/_chunks/tr-_DB1F1GW.mjs.map +1 -0
- package/dist/_chunks/uk-sI2I1ogF.js +49 -0
- package/dist/_chunks/uk-sI2I1ogF.js.map +1 -0
- package/dist/_chunks/uk-yxMSQAwI.mjs +49 -0
- package/dist/_chunks/uk-yxMSQAwI.mjs.map +1 -0
- package/dist/_chunks/vi-A3zJxaiI.js +50 -0
- package/dist/_chunks/vi-A3zJxaiI.js.map +1 -0
- package/dist/_chunks/vi-xY0zCW3d.mjs +50 -0
- package/dist/_chunks/vi-xY0zCW3d.mjs.map +1 -0
- package/dist/_chunks/zh-72SpmFXa.js +86 -0
- package/dist/_chunks/zh-72SpmFXa.js.map +1 -0
- package/dist/_chunks/zh-Hans-ArWWtyP4.js +86 -0
- package/dist/_chunks/zh-Hans-ArWWtyP4.js.map +1 -0
- package/dist/_chunks/zh-Hans-E84cu4kP.mjs +86 -0
- package/dist/_chunks/zh-Hans-E84cu4kP.mjs.map +1 -0
- package/dist/_chunks/zh-OFeldzbX.mjs +86 -0
- package/dist/_chunks/zh-OFeldzbX.mjs.map +1 -0
- package/dist/admin/index.js +5 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/index.mjs +6 -0
- package/dist/admin/index.mjs.map +1 -0
- package/documentation/content-api.yaml +23 -15
- package/jest.config.front.js +2 -0
- package/package.json +47 -30
- package/packup.config.ts +22 -0
- package/server/bootstrap/grant-config.js +9 -0
- package/server/bootstrap/index.js +36 -0
- package/server/config.js +29 -0
- package/server/controllers/auth.js +73 -18
- package/server/controllers/user.js +12 -1
- package/server/middlewares/rateLimit.js +41 -21
- package/server/register.js +7 -1
- package/server/services/providers-registry.js +16 -1
- package/server/strategies/users-permissions.js +1 -8
- package/.eslintrc.js +0 -14
- package/admin/src/hooks/index.js +0 -5
- package/admin/src/hooks/useFetchRole/index.js +0 -64
- package/admin/src/hooks/useFetchRole/reducer.js +0 -31
- package/admin/src/hooks/useForm/index.js +0 -70
- package/admin/src/hooks/useForm/reducer.js +0 -40
- package/admin/src/hooks/usePlugins/index.js +0 -67
- package/admin/src/hooks/usePlugins/init.js +0 -5
- package/admin/src/hooks/usePlugins/reducer.js +0 -34
- package/admin/src/hooks/useRolesList/index.js +0 -63
- package/admin/src/hooks/useRolesList/init.js +0 -5
- package/admin/src/hooks/useRolesList/reducer.js +0 -31
- package/admin/src/pages/AdvancedSettings/utils/api.js +0 -17
- package/admin/src/pages/EmailTemplates/utils/api.js +0 -17
- package/admin/src/pages/Providers/reducer.js +0 -54
- package/admin/src/pages/Providers/utils/api.js +0 -25
- package/admin/src/pages/Providers/utils/createProvidersArray.js +0 -21
- package/admin/src/pages/Roles/CreatePage/index.js +0 -182
- package/admin/src/pages/Roles/EditPage/index.js +0 -194
- package/admin/src/pages/Roles/EditPage/utils/schema.js +0 -9
- package/admin/src/pages/Roles/ProtectedCreatePage/index.js +0 -12
- package/admin/src/pages/Roles/ProtectedEditPage/index.js +0 -12
- package/admin/src/pages/Roles/ProtectedListPage/index.js +0 -15
- package/admin/src/pages/Roles/index.js +0 -27
- package/admin/src/utils/getRequestURL.js +0 -5
- package/strapi-admin.js +0 -3
- package/admin/src/components/Permissions/PermissionRow/{CheckboxWrapper.js → CheckboxWrapper.jsx} +1 -1
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
/* eslint-disable no-useless-escape */
|
|
10
10
|
const crypto = require('crypto');
|
|
11
11
|
const _ = require('lodash');
|
|
12
|
+
const { concat, compact, isArray } = require('lodash/fp');
|
|
12
13
|
const utils = require('@strapi/utils');
|
|
14
|
+
const {
|
|
15
|
+
contentTypes: { getNonWritableAttributes },
|
|
16
|
+
} = require('@strapi/utils');
|
|
13
17
|
const { getService } = require('../utils');
|
|
14
18
|
const {
|
|
15
19
|
validateCallbackBody,
|
|
@@ -22,7 +26,7 @@ const {
|
|
|
22
26
|
} = require('./validation/auth');
|
|
23
27
|
|
|
24
28
|
const { getAbsoluteAdminUrl, getAbsoluteServerUrl, sanitize } = utils;
|
|
25
|
-
const { ApplicationError, ValidationError } = utils.errors;
|
|
29
|
+
const { ApplicationError, ValidationError, ForbiddenError } = utils.errors;
|
|
26
30
|
|
|
27
31
|
const sanitizeUser = (user, ctx) => {
|
|
28
32
|
const { auth } = ctx.state;
|
|
@@ -96,6 +100,10 @@ module.exports = {
|
|
|
96
100
|
try {
|
|
97
101
|
const user = await getService('providers').connect(provider, ctx.query);
|
|
98
102
|
|
|
103
|
+
if (user.blocked) {
|
|
104
|
+
throw new ForbiddenError('Your account has been blocked by an administrator');
|
|
105
|
+
}
|
|
106
|
+
|
|
99
107
|
return ctx.send({
|
|
100
108
|
jwt: getService('jwt').issue({ id: user.id }),
|
|
101
109
|
user: await sanitizeUser(user, ctx),
|
|
@@ -193,10 +201,28 @@ module.exports = {
|
|
|
193
201
|
}
|
|
194
202
|
|
|
195
203
|
// Ability to pass OAuth callback dynamically
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
204
|
+
const queryCustomCallback = _.get(ctx, 'query.callback');
|
|
205
|
+
const dynamicSessionCallback = _.get(ctx, 'session.grant.dynamic.callback');
|
|
206
|
+
|
|
207
|
+
const customCallback = queryCustomCallback ?? dynamicSessionCallback;
|
|
208
|
+
|
|
209
|
+
// The custom callback is validated to make sure it's not redirecting to an unwanted actor.
|
|
210
|
+
if (customCallback !== undefined) {
|
|
211
|
+
try {
|
|
212
|
+
// We're extracting the callback validator from the plugin config since it can be user-customized
|
|
213
|
+
const { validate: validateCallback } = strapi
|
|
214
|
+
.plugin('users-permissions')
|
|
215
|
+
.config('callback');
|
|
216
|
+
|
|
217
|
+
await validateCallback(customCallback, grantConfig[provider]);
|
|
218
|
+
|
|
219
|
+
grantConfig[provider].callback = customCallback;
|
|
220
|
+
} catch (e) {
|
|
221
|
+
throw new ValidationError('Invalid callback URL provided', { callback: customCallback });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Build a valid redirect URI for the current provider
|
|
200
226
|
grantConfig[provider].redirect_uri = getService('providers').buildRedirectUri(provider);
|
|
201
227
|
|
|
202
228
|
return grant(grantConfig)(ctx, next);
|
|
@@ -273,20 +299,49 @@ module.exports = {
|
|
|
273
299
|
throw new ApplicationError('Register action is currently disabled');
|
|
274
300
|
}
|
|
275
301
|
|
|
302
|
+
const { register } = strapi.config.get('plugin.users-permissions');
|
|
303
|
+
const alwaysAllowedKeys = ['username', 'password', 'email'];
|
|
304
|
+
const userModel = strapi.contentTypes['plugin::users-permissions.user'];
|
|
305
|
+
const { attributes } = userModel;
|
|
306
|
+
|
|
307
|
+
const nonWritable = getNonWritableAttributes(userModel);
|
|
308
|
+
|
|
309
|
+
const allowedKeys = compact(
|
|
310
|
+
concat(
|
|
311
|
+
alwaysAllowedKeys,
|
|
312
|
+
isArray(register?.allowedFields)
|
|
313
|
+
? // Note that we do not filter allowedFields in case a user explicitly chooses to allow a private or otherwise omitted field on registration
|
|
314
|
+
register.allowedFields // if null or undefined, compact will remove it
|
|
315
|
+
: // to prevent breaking changes, if allowedFields is not set in config, we only remove private and known dangerous user schema fields
|
|
316
|
+
// TODO V5: allowedFields defaults to [] when undefined and remove this case
|
|
317
|
+
Object.keys(attributes).filter(
|
|
318
|
+
(key) =>
|
|
319
|
+
!nonWritable.includes(key) &&
|
|
320
|
+
!attributes[key].private &&
|
|
321
|
+
![
|
|
322
|
+
// many of these are included in nonWritable, but we'll list them again to be safe and since we're removing this code in v5 anyway
|
|
323
|
+
// Strapi user schema fields
|
|
324
|
+
'confirmed',
|
|
325
|
+
'blocked',
|
|
326
|
+
'confirmationToken',
|
|
327
|
+
'resetPasswordToken',
|
|
328
|
+
'provider',
|
|
329
|
+
'id',
|
|
330
|
+
'role',
|
|
331
|
+
// other Strapi fields that might be added
|
|
332
|
+
'createdAt',
|
|
333
|
+
'updatedAt',
|
|
334
|
+
'createdBy',
|
|
335
|
+
'updatedBy',
|
|
336
|
+
'publishedAt', // d&p
|
|
337
|
+
'strapi_reviewWorkflows_stage', // review workflows
|
|
338
|
+
].includes(key)
|
|
339
|
+
)
|
|
340
|
+
)
|
|
341
|
+
);
|
|
342
|
+
|
|
276
343
|
const params = {
|
|
277
|
-
..._.
|
|
278
|
-
'confirmed',
|
|
279
|
-
'blocked',
|
|
280
|
-
'confirmationToken',
|
|
281
|
-
'resetPasswordToken',
|
|
282
|
-
'provider',
|
|
283
|
-
'id',
|
|
284
|
-
'createdAt',
|
|
285
|
-
'updatedAt',
|
|
286
|
-
'createdBy',
|
|
287
|
-
'updatedBy',
|
|
288
|
-
'role',
|
|
289
|
-
]),
|
|
344
|
+
..._.pick(ctx.request.body, allowedKeys),
|
|
290
345
|
provider: 'local',
|
|
291
346
|
};
|
|
292
347
|
|
|
@@ -11,7 +11,7 @@ const utils = require('@strapi/utils');
|
|
|
11
11
|
const { getService } = require('../utils');
|
|
12
12
|
const { validateCreateUserBody, validateUpdateUserBody } = require('./validation/user');
|
|
13
13
|
|
|
14
|
-
const { sanitize } = utils;
|
|
14
|
+
const { sanitize, validate } = utils;
|
|
15
15
|
const { ApplicationError, ValidationError, NotFoundError } = utils.errors;
|
|
16
16
|
|
|
17
17
|
const sanitizeOutput = async (user, ctx) => {
|
|
@@ -21,6 +21,13 @@ const sanitizeOutput = async (user, ctx) => {
|
|
|
21
21
|
return sanitize.contentAPI.output(user, schema, { auth });
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
const validateQuery = async (query, ctx) => {
|
|
25
|
+
const schema = strapi.getModel('plugin::users-permissions.user');
|
|
26
|
+
const { auth } = ctx.state;
|
|
27
|
+
|
|
28
|
+
return validate.contentAPI.query(query, schema, { auth });
|
|
29
|
+
};
|
|
30
|
+
|
|
24
31
|
const sanitizeQuery = async (query, ctx) => {
|
|
25
32
|
const schema = strapi.getModel('plugin::users-permissions.user');
|
|
26
33
|
const { auth } = ctx.state;
|
|
@@ -143,6 +150,7 @@ module.exports = {
|
|
|
143
150
|
* @return {Object|Array}
|
|
144
151
|
*/
|
|
145
152
|
async find(ctx) {
|
|
153
|
+
await validateQuery(ctx.query, ctx);
|
|
146
154
|
const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);
|
|
147
155
|
const users = await getService('user').fetchAll(sanitizedQuery);
|
|
148
156
|
|
|
@@ -155,6 +163,7 @@ module.exports = {
|
|
|
155
163
|
*/
|
|
156
164
|
async findOne(ctx) {
|
|
157
165
|
const { id } = ctx.params;
|
|
166
|
+
await validateQuery(ctx.query, ctx);
|
|
158
167
|
const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);
|
|
159
168
|
|
|
160
169
|
let data = await getService('user').fetch(id, sanitizedQuery);
|
|
@@ -171,6 +180,7 @@ module.exports = {
|
|
|
171
180
|
* @return {Number}
|
|
172
181
|
*/
|
|
173
182
|
async count(ctx) {
|
|
183
|
+
await validateQuery(ctx.query, ctx);
|
|
174
184
|
const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);
|
|
175
185
|
|
|
176
186
|
ctx.body = await getService('user').count(sanitizedQuery);
|
|
@@ -201,6 +211,7 @@ module.exports = {
|
|
|
201
211
|
return ctx.unauthorized();
|
|
202
212
|
}
|
|
203
213
|
|
|
214
|
+
await validateQuery(query, ctx);
|
|
204
215
|
const sanitizedQuery = await sanitizeQuery(query, ctx);
|
|
205
216
|
const user = await getService('user').fetch(authUser.id, sanitizedQuery);
|
|
206
217
|
|
|
@@ -1,27 +1,47 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const utils = require('@strapi/utils');
|
|
5
|
+
const { isString, has, toLower } = require('lodash/fp');
|
|
6
|
+
|
|
7
|
+
const { RateLimitError } = utils.errors;
|
|
8
|
+
|
|
3
9
|
module.exports =
|
|
4
10
|
(config, { strapi }) =>
|
|
5
11
|
async (ctx, next) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
{
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
let rateLimitConfig = strapi.config.get('plugin.users-permissions.ratelimit');
|
|
13
|
+
|
|
14
|
+
if (!rateLimitConfig) {
|
|
15
|
+
rateLimitConfig = {
|
|
16
|
+
enabled: true,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!has('enabled', rateLimitConfig)) {
|
|
21
|
+
rateLimitConfig.enabled = true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (rateLimitConfig.enabled === true) {
|
|
25
|
+
const rateLimit = require('koa2-ratelimit').RateLimit;
|
|
26
|
+
|
|
27
|
+
const userIdentifier = toLower(ctx.request.body.email) || 'unknownIdentifier';
|
|
28
|
+
const requestPath = isString(ctx.request.path)
|
|
29
|
+
? toLower(path.normalize(ctx.request.path))
|
|
30
|
+
: 'invalidPath';
|
|
31
|
+
|
|
32
|
+
const loadConfig = {
|
|
33
|
+
interval: { min: 5 },
|
|
34
|
+
max: 5,
|
|
35
|
+
prefixKey: `${userIdentifier}:${requestPath}:${ctx.request.ip}`,
|
|
36
|
+
handler() {
|
|
37
|
+
throw new RateLimitError();
|
|
38
|
+
},
|
|
39
|
+
...rateLimitConfig,
|
|
40
|
+
...config,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return rateLimit.middleware(loadConfig)(ctx, next);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return next();
|
|
27
47
|
};
|
package/server/register.js
CHANGED
|
@@ -18,6 +18,12 @@ module.exports = ({ strapi }) => {
|
|
|
18
18
|
const specPath = path.join(__dirname, '../documentation/content-api.yaml');
|
|
19
19
|
const spec = fs.readFileSync(specPath, 'utf8');
|
|
20
20
|
|
|
21
|
-
strapi
|
|
21
|
+
strapi
|
|
22
|
+
.plugin('documentation')
|
|
23
|
+
.service('override')
|
|
24
|
+
.registerOverride(spec, {
|
|
25
|
+
pluginOrigin: 'users-permissions',
|
|
26
|
+
excludeFromGeneration: ['users-permissions'],
|
|
27
|
+
});
|
|
22
28
|
}
|
|
23
29
|
};
|
|
@@ -176,7 +176,7 @@ const getInitialProviders = ({ purest }) => ({
|
|
|
176
176
|
const vk = purest({ provider: 'vk' });
|
|
177
177
|
|
|
178
178
|
return vk
|
|
179
|
-
.get('users
|
|
179
|
+
.get('users')
|
|
180
180
|
.auth(accessToken)
|
|
181
181
|
.qs({ id: query.raw.user_id, v: '5.122' })
|
|
182
182
|
.request()
|
|
@@ -331,6 +331,21 @@ const getInitialProviders = ({ purest }) => ({
|
|
|
331
331
|
};
|
|
332
332
|
});
|
|
333
333
|
},
|
|
334
|
+
async keycloak({ accessToken, providers }) {
|
|
335
|
+
const keycloak = purest({ provider: 'keycloak' });
|
|
336
|
+
|
|
337
|
+
return keycloak
|
|
338
|
+
.subdomain(providers.keycloak.subdomain)
|
|
339
|
+
.get('protocol/openid-connect/userinfo')
|
|
340
|
+
.auth(accessToken)
|
|
341
|
+
.request()
|
|
342
|
+
.then(({ body }) => {
|
|
343
|
+
return {
|
|
344
|
+
username: body.preferred_username,
|
|
345
|
+
email: body.email,
|
|
346
|
+
};
|
|
347
|
+
});
|
|
348
|
+
},
|
|
334
349
|
});
|
|
335
350
|
|
|
336
351
|
module.exports = () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { castArray, map, every, pipe
|
|
3
|
+
const { castArray, map, every, pipe } = require('lodash/fp');
|
|
4
4
|
const { ForbiddenError, UnauthorizedError } = require('@strapi/utils').errors;
|
|
5
5
|
|
|
6
6
|
const { getService } = require('../utils');
|
|
@@ -80,13 +80,6 @@ const authenticate = async (ctx) => {
|
|
|
80
80
|
const verify = async (auth, config) => {
|
|
81
81
|
const { credentials: user, ability } = auth;
|
|
82
82
|
|
|
83
|
-
strapi.telemetry.send('didReceiveAPIRequest', {
|
|
84
|
-
eventProperties: {
|
|
85
|
-
authenticationMethod: auth?.strategy?.name,
|
|
86
|
-
isAuthenticated: !isEmpty(user),
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
|
|
90
83
|
if (!config.scope) {
|
|
91
84
|
if (!user) {
|
|
92
85
|
// A non authenticated user cannot access routes that do not have a scope
|
package/.eslintrc.js
DELETED
package/admin/src/hooks/index.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
// eslint-disable-next-line import/prefer-default-export
|
|
2
|
-
export { default as useForm } from './useForm';
|
|
3
|
-
export { default as useRolesList } from './useRolesList';
|
|
4
|
-
export { default as usePlugins } from './usePlugins';
|
|
5
|
-
export { default as useFetchRole } from './useFetchRole';
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { useCallback, useReducer, useEffect, useRef } from 'react';
|
|
2
|
-
import { useNotification, useFetchClient } from '@strapi/helper-plugin';
|
|
3
|
-
import reducer, { initialState } from './reducer';
|
|
4
|
-
import pluginId from '../../pluginId';
|
|
5
|
-
|
|
6
|
-
const useFetchRole = (id) => {
|
|
7
|
-
const [state, dispatch] = useReducer(reducer, initialState);
|
|
8
|
-
const toggleNotification = useNotification();
|
|
9
|
-
const isMounted = useRef(null);
|
|
10
|
-
const { get } = useFetchClient();
|
|
11
|
-
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
isMounted.current = true;
|
|
14
|
-
|
|
15
|
-
if (id) {
|
|
16
|
-
fetchRole(id);
|
|
17
|
-
} else {
|
|
18
|
-
dispatch({
|
|
19
|
-
type: 'GET_DATA_SUCCEEDED',
|
|
20
|
-
role: {},
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return () => (isMounted.current = false);
|
|
25
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
26
|
-
}, [id]);
|
|
27
|
-
|
|
28
|
-
const fetchRole = async (roleId) => {
|
|
29
|
-
try {
|
|
30
|
-
const {
|
|
31
|
-
data: { role },
|
|
32
|
-
} = await get(`/${pluginId}/roles/${roleId}`);
|
|
33
|
-
|
|
34
|
-
// Prevent updating state on an unmounted component
|
|
35
|
-
if (isMounted.current) {
|
|
36
|
-
dispatch({
|
|
37
|
-
type: 'GET_DATA_SUCCEEDED',
|
|
38
|
-
role,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
} catch (err) {
|
|
42
|
-
console.error(err);
|
|
43
|
-
|
|
44
|
-
dispatch({
|
|
45
|
-
type: 'GET_DATA_ERROR',
|
|
46
|
-
});
|
|
47
|
-
toggleNotification({
|
|
48
|
-
type: 'warning',
|
|
49
|
-
message: { id: 'notification.error' },
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const handleSubmitSucceeded = useCallback((data) => {
|
|
55
|
-
dispatch({
|
|
56
|
-
type: 'ON_SUBMIT_SUCCEEDED',
|
|
57
|
-
...data,
|
|
58
|
-
});
|
|
59
|
-
}, []);
|
|
60
|
-
|
|
61
|
-
return { ...state, onSubmitSucceeded: handleSubmitSucceeded };
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export default useFetchRole;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/* eslint-disable consistent-return */
|
|
2
|
-
import produce from 'immer';
|
|
3
|
-
|
|
4
|
-
export const initialState = {
|
|
5
|
-
role: {},
|
|
6
|
-
isLoading: true,
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
const reducer = (state, action) =>
|
|
10
|
-
produce(state, (draftState) => {
|
|
11
|
-
switch (action.type) {
|
|
12
|
-
case 'GET_DATA_SUCCEEDED': {
|
|
13
|
-
draftState.role = action.role;
|
|
14
|
-
draftState.isLoading = false;
|
|
15
|
-
break;
|
|
16
|
-
}
|
|
17
|
-
case 'GET_DATA_ERROR': {
|
|
18
|
-
draftState.isLoading = false;
|
|
19
|
-
break;
|
|
20
|
-
}
|
|
21
|
-
case 'ON_SUBMIT_SUCCEEDED': {
|
|
22
|
-
draftState.role.name = action.name;
|
|
23
|
-
draftState.role.description = action.description;
|
|
24
|
-
break;
|
|
25
|
-
}
|
|
26
|
-
default:
|
|
27
|
-
return draftState;
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
export default reducer;
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useReducer, useRef } from 'react';
|
|
2
|
-
import { useRBAC, request, useNotification } from '@strapi/helper-plugin';
|
|
3
|
-
import { getRequestURL } from '../../utils';
|
|
4
|
-
import reducer, { initialState } from './reducer';
|
|
5
|
-
|
|
6
|
-
const useUserForm = (endPoint, permissions) => {
|
|
7
|
-
const { isLoading: isLoadingForPermissions, allowedActions } = useRBAC(permissions);
|
|
8
|
-
const [{ isLoading, modifiedData }, dispatch] = useReducer(reducer, initialState);
|
|
9
|
-
const toggleNotification = useNotification();
|
|
10
|
-
const isMounted = useRef(true);
|
|
11
|
-
|
|
12
|
-
const abortController = new AbortController();
|
|
13
|
-
const { signal } = abortController;
|
|
14
|
-
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
const getData = async () => {
|
|
17
|
-
try {
|
|
18
|
-
dispatch({
|
|
19
|
-
type: 'GET_DATA',
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
const data = await request(getRequestURL(endPoint), { method: 'GET', signal });
|
|
23
|
-
|
|
24
|
-
dispatch({
|
|
25
|
-
type: 'GET_DATA_SUCCEEDED',
|
|
26
|
-
data,
|
|
27
|
-
});
|
|
28
|
-
} catch (err) {
|
|
29
|
-
// The user aborted the request
|
|
30
|
-
if (isMounted.current) {
|
|
31
|
-
dispatch({
|
|
32
|
-
type: 'GET_DATA_ERROR',
|
|
33
|
-
});
|
|
34
|
-
console.error(err);
|
|
35
|
-
toggleNotification({
|
|
36
|
-
type: 'warning',
|
|
37
|
-
message: { id: 'notification.error' },
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
if (!isLoadingForPermissions) {
|
|
44
|
-
getData();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return () => {
|
|
48
|
-
abortController.abort();
|
|
49
|
-
isMounted.current = false;
|
|
50
|
-
};
|
|
51
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
52
|
-
}, [isLoadingForPermissions, endPoint]);
|
|
53
|
-
|
|
54
|
-
const dispatchSubmitSucceeded = useCallback((data) => {
|
|
55
|
-
dispatch({
|
|
56
|
-
type: 'ON_SUBMIT_SUCCEEDED',
|
|
57
|
-
data,
|
|
58
|
-
});
|
|
59
|
-
}, []);
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
allowedActions,
|
|
63
|
-
dispatchSubmitSucceeded,
|
|
64
|
-
isLoading,
|
|
65
|
-
isLoadingForPermissions,
|
|
66
|
-
modifiedData,
|
|
67
|
-
};
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
export default useUserForm;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import produce from 'immer';
|
|
2
|
-
|
|
3
|
-
const initialState = {
|
|
4
|
-
isLoading: true,
|
|
5
|
-
modifiedData: {},
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
const reducer = (state, action) =>
|
|
9
|
-
// eslint-disable-next-line consistent-return
|
|
10
|
-
produce(state, (draftState) => {
|
|
11
|
-
switch (action.type) {
|
|
12
|
-
case 'GET_DATA': {
|
|
13
|
-
draftState.isLoading = true;
|
|
14
|
-
draftState.modifiedData = {};
|
|
15
|
-
|
|
16
|
-
break;
|
|
17
|
-
}
|
|
18
|
-
case 'GET_DATA_SUCCEEDED': {
|
|
19
|
-
draftState.isLoading = false;
|
|
20
|
-
draftState.modifiedData = action.data;
|
|
21
|
-
|
|
22
|
-
break;
|
|
23
|
-
}
|
|
24
|
-
case 'GET_DATA_ERROR': {
|
|
25
|
-
draftState.isLoading = true;
|
|
26
|
-
break;
|
|
27
|
-
}
|
|
28
|
-
case 'ON_SUBMIT_SUCCEEDED': {
|
|
29
|
-
draftState.modifiedData = action.data;
|
|
30
|
-
|
|
31
|
-
break;
|
|
32
|
-
}
|
|
33
|
-
default: {
|
|
34
|
-
return draftState;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
export default reducer;
|
|
40
|
-
export { initialState };
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useReducer } from 'react';
|
|
2
|
-
import { useNotification, useFetchClient } from '@strapi/helper-plugin';
|
|
3
|
-
import get from 'lodash/get';
|
|
4
|
-
import init from './init';
|
|
5
|
-
import pluginId from '../../pluginId';
|
|
6
|
-
import { cleanPermissions } from '../../utils';
|
|
7
|
-
import reducer, { initialState } from './reducer';
|
|
8
|
-
|
|
9
|
-
const usePlugins = (shouldFetchData = true) => {
|
|
10
|
-
const toggleNotification = useNotification();
|
|
11
|
-
const [{ permissions, routes, isLoading }, dispatch] = useReducer(reducer, initialState, () =>
|
|
12
|
-
init(initialState, shouldFetchData)
|
|
13
|
-
);
|
|
14
|
-
const fetchClient = useFetchClient();
|
|
15
|
-
|
|
16
|
-
const fetchPlugins = useCallback(async () => {
|
|
17
|
-
try {
|
|
18
|
-
dispatch({
|
|
19
|
-
type: 'GET_DATA',
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
const [{ permissions }, { routes }] = await Promise.all(
|
|
23
|
-
[`/${pluginId}/permissions`, `/${pluginId}/routes`].map(async (endpoint) => {
|
|
24
|
-
const res = await fetchClient.get(endpoint);
|
|
25
|
-
|
|
26
|
-
return res.data;
|
|
27
|
-
})
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
dispatch({
|
|
31
|
-
type: 'GET_DATA_SUCCEEDED',
|
|
32
|
-
permissions: cleanPermissions(permissions),
|
|
33
|
-
routes,
|
|
34
|
-
});
|
|
35
|
-
} catch (err) {
|
|
36
|
-
const message = get(err, ['response', 'payload', 'message'], 'An error occured');
|
|
37
|
-
|
|
38
|
-
dispatch({
|
|
39
|
-
type: 'GET_DATA_ERROR',
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
if (message !== 'Forbidden') {
|
|
43
|
-
toggleNotification({
|
|
44
|
-
type: 'warning',
|
|
45
|
-
message,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
51
|
-
}, [toggleNotification]);
|
|
52
|
-
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
if (shouldFetchData) {
|
|
55
|
-
fetchPlugins();
|
|
56
|
-
}
|
|
57
|
-
}, [fetchPlugins, shouldFetchData]);
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
permissions,
|
|
61
|
-
routes,
|
|
62
|
-
getData: fetchPlugins,
|
|
63
|
-
isLoading,
|
|
64
|
-
};
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export default usePlugins;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/* eslint-disable consistent-return */
|
|
2
|
-
import produce from 'immer';
|
|
3
|
-
|
|
4
|
-
export const initialState = {
|
|
5
|
-
permissions: {},
|
|
6
|
-
routes: {},
|
|
7
|
-
isLoading: true,
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const reducer = (state, action) =>
|
|
11
|
-
produce(state, (draftState) => {
|
|
12
|
-
switch (action.type) {
|
|
13
|
-
case 'GET_DATA': {
|
|
14
|
-
draftState.isLoading = true;
|
|
15
|
-
draftState.permissions = {};
|
|
16
|
-
draftState.routes = {};
|
|
17
|
-
break;
|
|
18
|
-
}
|
|
19
|
-
case 'GET_DATA_SUCCEEDED': {
|
|
20
|
-
draftState.permissions = action.permissions;
|
|
21
|
-
draftState.routes = action.routes;
|
|
22
|
-
draftState.isLoading = false;
|
|
23
|
-
break;
|
|
24
|
-
}
|
|
25
|
-
case 'GET_DATA_ERROR': {
|
|
26
|
-
draftState.isLoading = false;
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
default:
|
|
30
|
-
return draftState;
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
export default reducer;
|