@strapi/plugin-users-permissions 0.0.0-next.e3b4cdeebf6e9b0cd5575ff80b8a86715d44ce98 → 0.0.0-next.e61eff51f9834ffdef16bdc236aecab5f837723b

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.
Files changed (233) hide show
  1. package/.eslintignore +2 -2
  2. package/.eslintrc +17 -0
  3. package/LICENSE +18 -3
  4. package/admin/src/components/BoundRoute/{index.js → index.jsx} +7 -5
  5. package/admin/src/components/FormModal/Input/{index.js → index.jsx} +38 -34
  6. package/admin/src/components/FormModal/index.jsx +115 -0
  7. package/admin/src/components/Permissions/PermissionRow/{CheckboxWrapper.js → CheckboxWrapper.jsx} +4 -3
  8. package/admin/src/components/Permissions/PermissionRow/{SubCategory.js → SubCategory.jsx} +27 -15
  9. package/admin/src/components/Permissions/PermissionRow/{index.js → index.jsx} +4 -2
  10. package/admin/src/components/Permissions/index.jsx +47 -0
  11. package/admin/src/components/Permissions/reducer.js +1 -1
  12. package/admin/src/components/Policies/{index.js → index.jsx} +10 -7
  13. package/admin/src/components/UsersPermissions/{index.js → index.jsx} +22 -11
  14. package/admin/src/components/UsersPermissions/reducer.js +1 -1
  15. package/admin/src/{permissions.js → constants.js} +1 -3
  16. package/admin/src/contexts/UsersPermissionsContext/{index.js → index.jsx} +1 -0
  17. package/admin/src/index.js +27 -53
  18. package/admin/src/pages/AdvancedSettings/index.jsx +214 -0
  19. package/admin/src/pages/AdvancedSettings/utils/layout.js +20 -35
  20. package/admin/src/pages/AdvancedSettings/utils/schema.js +5 -2
  21. package/admin/src/pages/EmailTemplates/components/EmailForm.jsx +156 -0
  22. package/admin/src/pages/EmailTemplates/components/{EmailTable.js → EmailTable.jsx} +28 -23
  23. package/admin/src/pages/EmailTemplates/index.jsx +148 -0
  24. package/admin/src/pages/EmailTemplates/utils/schema.js +18 -6
  25. package/admin/src/pages/Providers/index.jsx +262 -0
  26. package/admin/src/pages/Providers/utils/forms.js +23 -11
  27. package/admin/src/pages/Roles/constants.js +7 -0
  28. package/admin/src/pages/Roles/hooks/usePlugins.js +78 -0
  29. package/admin/src/pages/Roles/index.jsx +24 -0
  30. package/admin/src/pages/Roles/pages/CreatePage.jsx +194 -0
  31. package/admin/src/pages/Roles/pages/EditPage.jsx +215 -0
  32. package/admin/src/pages/Roles/pages/ListPage/components/TableBody.jsx +119 -0
  33. package/admin/src/pages/Roles/{ListPage/index.js → pages/ListPage/index.jsx} +108 -83
  34. package/admin/src/pluginId.js +2 -2
  35. package/admin/src/translations/en.json +1 -1
  36. package/admin/src/translations/uk.json +41 -4
  37. package/admin/src/translations/zh-Hans.json +80 -80
  38. package/admin/src/utils/index.js +1 -2
  39. package/admin/src/utils/prefixPluginTranslations.js +13 -0
  40. package/dist/admin/chunks/ar-BJwjobLp.js +45 -0
  41. package/dist/admin/chunks/ar-BJwjobLp.js.map +1 -0
  42. package/dist/admin/chunks/ar-G6bUGuUb.mjs +43 -0
  43. package/dist/admin/chunks/ar-G6bUGuUb.mjs.map +1 -0
  44. package/dist/admin/chunks/cs-Bu59JqhG.mjs +49 -0
  45. package/dist/admin/chunks/cs-Bu59JqhG.mjs.map +1 -0
  46. package/dist/admin/chunks/cs-uS_SIEo8.js +51 -0
  47. package/dist/admin/chunks/cs-uS_SIEo8.js.map +1 -0
  48. package/dist/admin/chunks/de-7MVMrqqI.js +63 -0
  49. package/dist/admin/chunks/de-7MVMrqqI.js.map +1 -0
  50. package/dist/admin/chunks/de-B81A69_5.mjs +61 -0
  51. package/dist/admin/chunks/de-B81A69_5.mjs.map +1 -0
  52. package/dist/admin/chunks/dk-BaelzvBE.mjs +85 -0
  53. package/dist/admin/chunks/dk-BaelzvBE.mjs.map +1 -0
  54. package/dist/admin/chunks/dk-DwCLGmy9.js +87 -0
  55. package/dist/admin/chunks/dk-DwCLGmy9.js.map +1 -0
  56. package/dist/admin/chunks/en-BhgCBe7M.mjs +85 -0
  57. package/dist/admin/chunks/en-BhgCBe7M.mjs.map +1 -0
  58. package/dist/admin/chunks/en-DwQjkHi_.js +87 -0
  59. package/dist/admin/chunks/en-DwQjkHi_.js.map +1 -0
  60. package/dist/admin/chunks/es-B0wXmvRj.mjs +85 -0
  61. package/dist/admin/chunks/es-B0wXmvRj.mjs.map +1 -0
  62. package/dist/admin/chunks/es-BOJOedG5.js +87 -0
  63. package/dist/admin/chunks/es-BOJOedG5.js.map +1 -0
  64. package/dist/admin/chunks/fr-BDNWCNs0.js +51 -0
  65. package/dist/admin/chunks/fr-BDNWCNs0.js.map +1 -0
  66. package/dist/admin/chunks/fr-CGYvGUXg.mjs +49 -0
  67. package/dist/admin/chunks/fr-CGYvGUXg.mjs.map +1 -0
  68. package/dist/admin/chunks/id-CNzbwFjA.mjs +61 -0
  69. package/dist/admin/chunks/id-CNzbwFjA.mjs.map +1 -0
  70. package/dist/admin/chunks/id-UqUPykHZ.js +63 -0
  71. package/dist/admin/chunks/id-UqUPykHZ.js.map +1 -0
  72. package/dist/admin/chunks/index--_o6btSC.js +471 -0
  73. package/dist/admin/chunks/index--_o6btSC.js.map +1 -0
  74. package/dist/admin/chunks/index-BBjNJt_G.mjs +448 -0
  75. package/dist/admin/chunks/index-BBjNJt_G.mjs.map +1 -0
  76. package/dist/admin/chunks/index-BgAfLcWs.mjs +1516 -0
  77. package/dist/admin/chunks/index-BgAfLcWs.mjs.map +1 -0
  78. package/dist/admin/chunks/index-CHTUC0LM.mjs +718 -0
  79. package/dist/admin/chunks/index-CHTUC0LM.mjs.map +1 -0
  80. package/dist/admin/chunks/index-CY5JZ38k.mjs +213 -0
  81. package/dist/admin/chunks/index-CY5JZ38k.mjs.map +1 -0
  82. package/dist/admin/chunks/index-Cu1VuLS3.js +741 -0
  83. package/dist/admin/chunks/index-Cu1VuLS3.js.map +1 -0
  84. package/dist/admin/chunks/index-Cy-tPfRk.mjs +279 -0
  85. package/dist/admin/chunks/index-Cy-tPfRk.mjs.map +1 -0
  86. package/dist/admin/chunks/index-DD-Z6c1S.js +217 -0
  87. package/dist/admin/chunks/index-DD-Z6c1S.js.map +1 -0
  88. package/dist/admin/chunks/index-DFnYIqrB.js +301 -0
  89. package/dist/admin/chunks/index-DFnYIqrB.js.map +1 -0
  90. package/dist/admin/chunks/index-DvubCYNe.js +1537 -0
  91. package/dist/admin/chunks/index-DvubCYNe.js.map +1 -0
  92. package/dist/admin/chunks/it-B2H2foTf.mjs +61 -0
  93. package/dist/admin/chunks/it-B2H2foTf.mjs.map +1 -0
  94. package/dist/admin/chunks/it-D5VuyoLU.js +63 -0
  95. package/dist/admin/chunks/it-D5VuyoLU.js.map +1 -0
  96. package/dist/admin/chunks/ja-C0z9d7L9.mjs +47 -0
  97. package/dist/admin/chunks/ja-C0z9d7L9.mjs.map +1 -0
  98. package/dist/admin/chunks/ja-MpqVsCgs.js +49 -0
  99. package/dist/admin/chunks/ja-MpqVsCgs.js.map +1 -0
  100. package/dist/admin/chunks/ko-Bm-grPSc.js +87 -0
  101. package/dist/admin/chunks/ko-Bm-grPSc.js.map +1 -0
  102. package/dist/admin/chunks/ko-CzUgzpeS.mjs +85 -0
  103. package/dist/admin/chunks/ko-CzUgzpeS.mjs.map +1 -0
  104. package/dist/admin/chunks/ms-CCacxjim.mjs +48 -0
  105. package/dist/admin/chunks/ms-CCacxjim.mjs.map +1 -0
  106. package/dist/admin/chunks/ms-D7eyBD5H.js +50 -0
  107. package/dist/admin/chunks/ms-D7eyBD5H.js.map +1 -0
  108. package/dist/admin/chunks/nl-BIOwAQtI.js +49 -0
  109. package/dist/admin/chunks/nl-BIOwAQtI.js.map +1 -0
  110. package/dist/admin/chunks/nl-DDC3nZW-.mjs +47 -0
  111. package/dist/admin/chunks/nl-DDC3nZW-.mjs.map +1 -0
  112. package/dist/admin/chunks/pl-D5BeNrg_.js +87 -0
  113. package/dist/admin/chunks/pl-D5BeNrg_.js.map +1 -0
  114. package/dist/admin/chunks/pl-XkS463rN.mjs +85 -0
  115. package/dist/admin/chunks/pl-XkS463rN.mjs.map +1 -0
  116. package/dist/admin/chunks/pt-BR-8cC7z8Km.mjs +43 -0
  117. package/dist/admin/chunks/pt-BR-8cC7z8Km.mjs.map +1 -0
  118. package/dist/admin/chunks/pt-BR-DxPBzQGx.js +45 -0
  119. package/dist/admin/chunks/pt-BR-DxPBzQGx.js.map +1 -0
  120. package/dist/admin/chunks/pt-DQpEvio8.mjs +47 -0
  121. package/dist/admin/chunks/pt-DQpEvio8.mjs.map +1 -0
  122. package/dist/admin/chunks/pt-kkCwzNvH.js +49 -0
  123. package/dist/admin/chunks/pt-kkCwzNvH.js.map +1 -0
  124. package/dist/admin/chunks/ru-BQ0gHmp3.js +87 -0
  125. package/dist/admin/chunks/ru-BQ0gHmp3.js.map +1 -0
  126. package/dist/admin/chunks/ru-nzL_7Mhg.mjs +85 -0
  127. package/dist/admin/chunks/ru-nzL_7Mhg.mjs.map +1 -0
  128. package/dist/admin/chunks/sk-Ddxc_tZA.mjs +49 -0
  129. package/dist/admin/chunks/sk-Ddxc_tZA.mjs.map +1 -0
  130. package/dist/admin/chunks/sk-nVwAPdYC.js +51 -0
  131. package/dist/admin/chunks/sk-nVwAPdYC.js.map +1 -0
  132. package/dist/admin/chunks/sv-BDfk2A-F.js +87 -0
  133. package/dist/admin/chunks/sv-BDfk2A-F.js.map +1 -0
  134. package/dist/admin/chunks/sv-By3RYpMG.mjs +85 -0
  135. package/dist/admin/chunks/sv-By3RYpMG.mjs.map +1 -0
  136. package/dist/admin/chunks/th-BtTtpHe2.js +61 -0
  137. package/dist/admin/chunks/th-BtTtpHe2.js.map +1 -0
  138. package/dist/admin/chunks/th-COl50vqb.mjs +59 -0
  139. package/dist/admin/chunks/th-COl50vqb.mjs.map +1 -0
  140. package/dist/admin/chunks/tr-80SJU6jg.mjs +84 -0
  141. package/dist/admin/chunks/tr-80SJU6jg.mjs.map +1 -0
  142. package/dist/admin/chunks/tr-Di-Nf7cT.js +86 -0
  143. package/dist/admin/chunks/tr-Di-Nf7cT.js.map +1 -0
  144. package/dist/admin/chunks/uk-DnrIlPwG.mjs +85 -0
  145. package/dist/admin/chunks/uk-DnrIlPwG.mjs.map +1 -0
  146. package/dist/admin/chunks/uk-r5zXTAS7.js +87 -0
  147. package/dist/admin/chunks/uk-r5zXTAS7.js.map +1 -0
  148. package/dist/admin/chunks/vi-69AF03Iv.mjs +49 -0
  149. package/dist/admin/chunks/vi-69AF03Iv.mjs.map +1 -0
  150. package/dist/admin/chunks/vi-D9cCsHsU.js +51 -0
  151. package/dist/admin/chunks/vi-D9cCsHsU.js.map +1 -0
  152. package/dist/admin/chunks/zh-BzSkqxo-.mjs +85 -0
  153. package/dist/admin/chunks/zh-BzSkqxo-.mjs.map +1 -0
  154. package/dist/admin/chunks/zh-BzWgJEzz.js +87 -0
  155. package/dist/admin/chunks/zh-BzWgJEzz.js.map +1 -0
  156. package/dist/admin/chunks/zh-Hans-CKqQbpsM.js +87 -0
  157. package/dist/admin/chunks/zh-Hans-CKqQbpsM.js.map +1 -0
  158. package/dist/admin/chunks/zh-Hans-DmDcSsp7.mjs +85 -0
  159. package/dist/admin/chunks/zh-Hans-DmDcSsp7.mjs.map +1 -0
  160. package/dist/admin/index.js +8 -0
  161. package/dist/admin/index.js.map +1 -0
  162. package/dist/admin/index.mjs +2 -0
  163. package/dist/admin/index.mjs.map +1 -0
  164. package/dist/server/index.js +4766 -0
  165. package/dist/server/index.js.map +1 -0
  166. package/dist/server/index.mjs +4764 -0
  167. package/dist/server/index.mjs.map +1 -0
  168. package/documentation/content-api.yaml +1 -1
  169. package/jest.config.front.js +1 -0
  170. package/package.json +53 -37
  171. package/rollup.config.mjs +52 -0
  172. package/server/bootstrap/index.js +18 -15
  173. package/server/bootstrap/users-permissions-actions.js +6 -0
  174. package/server/config.js +29 -0
  175. package/server/content-types/user/index.js +0 -1
  176. package/server/controllers/auth.js +75 -39
  177. package/server/controllers/content-manager-user.js +28 -30
  178. package/server/controllers/role.js +17 -4
  179. package/server/controllers/user.js +18 -8
  180. package/server/controllers/validation/auth.js +104 -25
  181. package/server/graphql/types/index.js +1 -0
  182. package/server/graphql/types/me.js +1 -0
  183. package/server/graphql/types/user-input.js +20 -0
  184. package/server/middlewares/rateLimit.js +41 -21
  185. package/server/register.js +2 -2
  186. package/server/services/jwt.js +3 -3
  187. package/server/services/permission.js +3 -7
  188. package/server/services/providers-registry.js +469 -261
  189. package/server/services/providers.js +10 -5
  190. package/server/services/role.js +15 -13
  191. package/server/services/user.js +56 -19
  192. package/server/services/users-permissions.js +15 -13
  193. package/server/strategies/users-permissions.js +1 -8
  194. package/server/utils/index.d.ts +2 -1
  195. package/server/utils/sanitize/sanitizers.js +7 -3
  196. package/server/utils/sanitize/visitors/remove-user-relation-from-role-entities.js +2 -2
  197. package/.eslintrc.js +0 -14
  198. package/admin/src/components/FormModal/index.js +0 -123
  199. package/admin/src/components/Permissions/index.js +0 -54
  200. package/admin/src/hooks/index.js +0 -5
  201. package/admin/src/hooks/useFetchRole/index.js +0 -64
  202. package/admin/src/hooks/useFetchRole/reducer.js +0 -31
  203. package/admin/src/hooks/useForm/index.js +0 -67
  204. package/admin/src/hooks/useForm/reducer.js +0 -40
  205. package/admin/src/hooks/usePlugins/index.js +0 -67
  206. package/admin/src/hooks/usePlugins/init.js +0 -5
  207. package/admin/src/hooks/usePlugins/reducer.js +0 -34
  208. package/admin/src/hooks/useRolesList/index.js +0 -62
  209. package/admin/src/hooks/useRolesList/init.js +0 -5
  210. package/admin/src/hooks/useRolesList/reducer.js +0 -31
  211. package/admin/src/pages/AdvancedSettings/index.js +0 -243
  212. package/admin/src/pages/AdvancedSettings/utils/api.js +0 -17
  213. package/admin/src/pages/EmailTemplates/components/EmailForm.js +0 -175
  214. package/admin/src/pages/EmailTemplates/index.js +0 -160
  215. package/admin/src/pages/EmailTemplates/utils/api.js +0 -17
  216. package/admin/src/pages/Providers/index.js +0 -272
  217. package/admin/src/pages/Providers/reducer.js +0 -54
  218. package/admin/src/pages/Providers/utils/api.js +0 -25
  219. package/admin/src/pages/Providers/utils/createProvidersArray.js +0 -21
  220. package/admin/src/pages/Roles/CreatePage/index.js +0 -182
  221. package/admin/src/pages/Roles/CreatePage/utils/schema.js +0 -9
  222. package/admin/src/pages/Roles/EditPage/index.js +0 -194
  223. package/admin/src/pages/Roles/EditPage/utils/schema.js +0 -9
  224. package/admin/src/pages/Roles/ListPage/components/TableBody.js +0 -92
  225. package/admin/src/pages/Roles/ListPage/utils/api.js +0 -31
  226. package/admin/src/pages/Roles/ProtectedCreatePage/index.js +0 -12
  227. package/admin/src/pages/Roles/ProtectedEditPage/index.js +0 -12
  228. package/admin/src/pages/Roles/ProtectedListPage/index.js +0 -15
  229. package/admin/src/pages/Roles/index.js +0 -27
  230. package/admin/src/utils/getRequestURL.js +0 -5
  231. package/server/bootstrap/grant-config.js +0 -131
  232. package/strapi-admin.js +0 -3
  233. package/strapi-server.js +0 -3
@@ -17,24 +17,25 @@ const ACTIONS = {
17
17
  };
18
18
 
19
19
  const findEntityAndCheckPermissions = async (ability, action, model, id) => {
20
- const entity = await strapi.query(userModel).findOne({
21
- where: { id },
20
+ const doc = await strapi.service('plugin::content-manager.document-manager').findOne(id, model, {
22
21
  populate: [`${CREATED_BY_ATTRIBUTE}.roles`],
23
22
  });
24
23
 
25
- if (_.isNil(entity)) {
24
+ if (_.isNil(doc)) {
26
25
  throw new NotFoundError();
27
26
  }
28
27
 
29
- const pm = strapi.admin.services.permission.createPermissionsManager({ ability, action, model });
28
+ const pm = strapi
29
+ .service('admin::permission')
30
+ .createPermissionsManager({ ability, action, model });
30
31
 
31
- if (pm.ability.cannot(pm.action, pm.toSubject(entity))) {
32
+ if (pm.ability.cannot(pm.action, pm.toSubject(doc))) {
32
33
  throw new ForbiddenError();
33
34
  }
34
35
 
35
- const entityWithoutCreatorRoles = _.omit(entity, `${CREATED_BY_ATTRIBUTE}.roles`);
36
+ const docWithoutCreatorRoles = _.omit(doc, `${CREATED_BY_ATTRIBUTE}.roles`);
36
37
 
37
- return { pm, entity: entityWithoutCreatorRoles };
38
+ return { pm, doc: docWithoutCreatorRoles };
38
39
  };
39
40
 
40
41
  module.exports = {
@@ -48,7 +49,7 @@ module.exports = {
48
49
 
49
50
  const { email, username } = body;
50
51
 
51
- const pm = strapi.admin.services.permission.createPermissionsManager({
52
+ const pm = strapi.service('admin::permission').createPermissionsManager({
52
53
  ability: userAbility,
53
54
  action: ACTIONS.create,
54
55
  model: userModel,
@@ -66,7 +67,7 @@ module.exports = {
66
67
 
67
68
  await validateCreateUserBody(ctx.request.body);
68
69
 
69
- const userWithSameUsername = await strapi
70
+ const userWithSameUsername = await strapi.db
70
71
  .query('plugin::users-permissions.user')
71
72
  .findOne({ where: { username } });
72
73
 
@@ -75,7 +76,7 @@ module.exports = {
75
76
  }
76
77
 
77
78
  if (advanced.unique_email) {
78
- const userWithSameEmail = await strapi
79
+ const userWithSameEmail = await strapi.db
79
80
  .query('plugin::users-permissions.user')
80
81
  .findOne({ where: { email: email.toLowerCase() } });
81
82
 
@@ -93,18 +94,11 @@ module.exports = {
93
94
 
94
95
  user.email = _.toLower(user.email);
95
96
 
96
- if (!user.role) {
97
- const defaultRole = await strapi
98
- .query('plugin::users-permissions.role')
99
- .findOne({ where: { type: advanced.default_role } });
100
-
101
- user.role = defaultRole.id;
102
- }
103
-
104
97
  try {
105
98
  const data = await strapi
106
- .service('plugin::content-manager.entity-manager')
107
- .create(user, userModel);
99
+ .service('plugin::content-manager.document-manager')
100
+ .create(userModel, { data: user });
101
+
108
102
  const sanitizedData = await pm.sanitizeOutput(data, { action: ACTIONS.read });
109
103
 
110
104
  ctx.created(sanitizedData);
@@ -118,7 +112,7 @@ module.exports = {
118
112
  */
119
113
 
120
114
  async update(ctx) {
121
- const { id } = ctx.params;
115
+ const { id: documentId } = ctx.params;
122
116
  const { body } = ctx.request;
123
117
  const { user: admin, userAbility } = ctx.state;
124
118
 
@@ -128,13 +122,14 @@ module.exports = {
128
122
 
129
123
  const { email, username, password } = body;
130
124
 
131
- const { pm, entity } = await findEntityAndCheckPermissions(
125
+ const { pm, doc } = await findEntityAndCheckPermissions(
132
126
  userAbility,
133
127
  ACTIONS.edit,
134
128
  userModel,
135
- id
129
+ documentId
136
130
  );
137
- const user = entity;
131
+
132
+ const user = doc;
138
133
 
139
134
  await validateUpdateUserBody(ctx.request.body);
140
135
 
@@ -143,23 +138,24 @@ module.exports = {
143
138
  }
144
139
 
145
140
  if (_.has(body, 'username')) {
146
- const userWithSameUsername = await strapi
141
+ const userWithSameUsername = await strapi.db
147
142
  .query('plugin::users-permissions.user')
148
143
  .findOne({ where: { username } });
149
144
 
150
- if (userWithSameUsername && _.toString(userWithSameUsername.id) !== _.toString(id)) {
145
+ if (userWithSameUsername && _.toString(userWithSameUsername.id) !== _.toString(user.id)) {
151
146
  throw new ApplicationError('Username already taken');
152
147
  }
153
148
  }
154
149
 
155
150
  if (_.has(body, 'email') && advancedConfigs.unique_email) {
156
- const userWithSameEmail = await strapi
151
+ const userWithSameEmail = await strapi.db
157
152
  .query('plugin::users-permissions.user')
158
153
  .findOne({ where: { email: _.toLower(email) } });
159
154
 
160
- if (userWithSameEmail && _.toString(userWithSameEmail.id) !== _.toString(id)) {
155
+ if (userWithSameEmail && _.toString(userWithSameEmail.id) !== _.toString(user.id)) {
161
156
  throw new ApplicationError('Email already taken');
162
157
  }
158
+
163
159
  body.email = _.toLower(body.email);
164
160
  }
165
161
 
@@ -167,8 +163,10 @@ module.exports = {
167
163
  const updateData = _.omit({ ...sanitizedData, updatedBy: admin.id }, 'createdBy');
168
164
 
169
165
  const data = await strapi
170
- .service('plugin::content-manager.entity-manager')
171
- .update({ id }, updateData, userModel);
166
+ .service('plugin::content-manager.document-manager')
167
+ .update(documentId, userModel, {
168
+ data: updateData,
169
+ });
172
170
 
173
171
  ctx.body = await pm.sanitizeOutput(data, { action: ACTIONS.read });
174
172
  },
@@ -1,10 +1,19 @@
1
1
  'use strict';
2
2
 
3
3
  const _ = require('lodash');
4
- const { ApplicationError, ValidationError } = require('@strapi/utils').errors;
4
+ const { async, errors } = require('@strapi/utils');
5
5
  const { getService } = require('../utils');
6
6
  const { validateDeleteRoleBody } = require('./validation/user');
7
7
 
8
+ const { ApplicationError, ValidationError } = errors;
9
+
10
+ const sanitizeOutput = async (role) => {
11
+ const { sanitizeLocalizationFields } = strapi.plugin('i18n').service('sanitize');
12
+ const schema = strapi.getModel('plugin::users-permissions.role');
13
+
14
+ return async.pipe(sanitizeLocalizationFields(schema))(role);
15
+ };
16
+
8
17
  module.exports = {
9
18
  /**
10
19
  * Default action.
@@ -30,13 +39,17 @@ module.exports = {
30
39
  return ctx.notFound();
31
40
  }
32
41
 
33
- ctx.send({ role });
42
+ const safeRole = await sanitizeOutput(role);
43
+
44
+ ctx.send({ role: safeRole });
34
45
  },
35
46
 
36
47
  async find(ctx) {
37
48
  const roles = await getService('role').find();
38
49
 
39
- ctx.send({ roles });
50
+ const safeRoles = await Promise.all(roles.map(sanitizeOutput));
51
+
52
+ ctx.send({ roles: safeRoles });
40
53
  },
41
54
 
42
55
  async updateRole(ctx) {
@@ -59,7 +72,7 @@ module.exports = {
59
72
  }
60
73
 
61
74
  // Fetch public role.
62
- const publicRole = await strapi
75
+ const publicRole = await strapi.db
63
76
  .query('plugin::users-permissions.role')
64
77
  .findOne({ where: { type: 'public' } });
65
78
 
@@ -11,21 +11,27 @@ 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;
15
14
  const { ApplicationError, ValidationError, NotFoundError } = utils.errors;
16
15
 
17
16
  const sanitizeOutput = async (user, ctx) => {
18
17
  const schema = strapi.getModel('plugin::users-permissions.user');
19
18
  const { auth } = ctx.state;
20
19
 
21
- return sanitize.contentAPI.output(user, schema, { auth });
20
+ return strapi.contentAPI.sanitize.output(user, schema, { auth });
21
+ };
22
+
23
+ const validateQuery = async (query, ctx) => {
24
+ const schema = strapi.getModel('plugin::users-permissions.user');
25
+ const { auth } = ctx.state;
26
+
27
+ return strapi.contentAPI.validate.query(query, schema, { auth });
22
28
  };
23
29
 
24
30
  const sanitizeQuery = async (query, ctx) => {
25
31
  const schema = strapi.getModel('plugin::users-permissions.user');
26
32
  const { auth } = ctx.state;
27
33
 
28
- return sanitize.contentAPI.query(query, schema, { auth });
34
+ return strapi.contentAPI.sanitize.query(query, schema, { auth });
29
35
  };
30
36
 
31
37
  module.exports = {
@@ -42,7 +48,7 @@ module.exports = {
42
48
 
43
49
  const { email, username, role } = ctx.request.body;
44
50
 
45
- const userWithSameUsername = await strapi
51
+ const userWithSameUsername = await strapi.db
46
52
  .query('plugin::users-permissions.user')
47
53
  .findOne({ where: { username } });
48
54
 
@@ -51,7 +57,7 @@ module.exports = {
51
57
  }
52
58
 
53
59
  if (advanced.unique_email) {
54
- const userWithSameEmail = await strapi
60
+ const userWithSameEmail = await strapi.db
55
61
  .query('plugin::users-permissions.user')
56
62
  .findOne({ where: { email: email.toLowerCase() } });
57
63
 
@@ -67,7 +73,7 @@ module.exports = {
67
73
  };
68
74
 
69
75
  if (!role) {
70
- const defaultRole = await strapi
76
+ const defaultRole = await strapi.db
71
77
  .query('plugin::users-permissions.role')
72
78
  .findOne({ where: { type: advanced.default_role } });
73
79
 
@@ -108,7 +114,7 @@ module.exports = {
108
114
  }
109
115
 
110
116
  if (_.has(ctx.request.body, 'username')) {
111
- const userWithSameUsername = await strapi
117
+ const userWithSameUsername = await strapi.db
112
118
  .query('plugin::users-permissions.user')
113
119
  .findOne({ where: { username } });
114
120
 
@@ -118,7 +124,7 @@ module.exports = {
118
124
  }
119
125
 
120
126
  if (_.has(ctx.request.body, 'email') && advancedConfigs.unique_email) {
121
- const userWithSameEmail = await strapi
127
+ const userWithSameEmail = await strapi.db
122
128
  .query('plugin::users-permissions.user')
123
129
  .findOne({ where: { email: email.toLowerCase() } });
124
130
 
@@ -143,6 +149,7 @@ module.exports = {
143
149
  * @return {Object|Array}
144
150
  */
145
151
  async find(ctx) {
152
+ await validateQuery(ctx.query, ctx);
146
153
  const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);
147
154
  const users = await getService('user').fetchAll(sanitizedQuery);
148
155
 
@@ -155,6 +162,7 @@ module.exports = {
155
162
  */
156
163
  async findOne(ctx) {
157
164
  const { id } = ctx.params;
165
+ await validateQuery(ctx.query, ctx);
158
166
  const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);
159
167
 
160
168
  let data = await getService('user').fetch(id, sanitizedQuery);
@@ -171,6 +179,7 @@ module.exports = {
171
179
  * @return {Number}
172
180
  */
173
181
  async count(ctx) {
182
+ await validateQuery(ctx.query, ctx);
174
183
  const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);
175
184
 
176
185
  ctx.body = await getService('user').count(sanitizedQuery);
@@ -201,6 +210,7 @@ module.exports = {
201
210
  return ctx.unauthorized();
202
211
  }
203
212
 
213
+ await validateQuery(query, ctx);
204
214
  const sanitizedQuery = await sanitizeQuery(query, ctx);
205
215
  const user = await getService('user').fetch(authUser.id, sanitizedQuery);
206
216
 
@@ -7,11 +7,35 @@ const callbackSchema = yup.object({
7
7
  password: yup.string().required(),
8
8
  });
9
9
 
10
- const registerSchema = yup.object({
11
- email: yup.string().email().required(),
12
- username: yup.string().required(),
13
- password: yup.string().required(),
14
- });
10
+ const createRegisterSchema = (config) =>
11
+ yup.object({
12
+ email: yup.string().email().required(),
13
+ username: yup.string().required(),
14
+ password: yup
15
+ .string()
16
+ .required()
17
+ .test(function (value) {
18
+ if (!value) return true;
19
+ const isValid = new TextEncoder().encode(value).length <= 72;
20
+ if (!isValid) {
21
+ return this.createError({ message: 'Password must be less than 73 bytes' });
22
+ }
23
+ return true;
24
+ })
25
+ .test(async function (value) {
26
+ if (typeof config?.validatePassword === 'function') {
27
+ try {
28
+ const isValid = await config.validatePassword(value);
29
+ if (!isValid) {
30
+ return this.createError({ message: 'Password validation failed.' });
31
+ }
32
+ } catch (error) {
33
+ return this.createError({ message: error.message || 'An error occurred.' });
34
+ }
35
+ }
36
+ return true;
37
+ }),
38
+ });
15
39
 
16
40
  const sendEmailConfirmationSchema = yup.object({
17
41
  email: yup.string().email().required(),
@@ -27,31 +51,86 @@ const forgotPasswordSchema = yup
27
51
  })
28
52
  .noUnknown();
29
53
 
30
- const resetPasswordSchema = yup
31
- .object({
32
- password: yup.string().required(),
33
- passwordConfirmation: yup.string().required(),
34
- code: yup.string().required(),
35
- })
36
- .noUnknown();
54
+ const createResetPasswordSchema = (config) =>
55
+ yup
56
+ .object({
57
+ password: yup
58
+ .string()
59
+ .required()
60
+ .test(function (value) {
61
+ if (!value) return true;
62
+ const isValid = new TextEncoder().encode(value).length <= 72;
63
+ if (!isValid) {
64
+ return this.createError({ message: 'Password must be less than 73 bytes' });
65
+ }
66
+ return true;
67
+ })
68
+ .test(async function (value) {
69
+ if (typeof config?.validatePassword === 'function') {
70
+ try {
71
+ const isValid = await config.validatePassword(value);
72
+ if (!isValid) {
73
+ return this.createError({ message: 'Password validation failed.' });
74
+ }
75
+ } catch (error) {
76
+ return this.createError({ message: error.message || 'An error occurred.' });
77
+ }
78
+ }
79
+ return true;
80
+ }),
81
+ passwordConfirmation: yup
82
+ .string()
83
+ .required()
84
+ .oneOf([yup.ref('password')], 'Passwords do not match'),
37
85
 
38
- const changePasswordSchema = yup
39
- .object({
40
- password: yup.string().required(),
41
- passwordConfirmation: yup
42
- .string()
43
- .required()
44
- .oneOf([yup.ref('password')], 'Passwords do not match'),
45
- currentPassword: yup.string().required(),
46
- })
47
- .noUnknown();
86
+ code: yup.string().required(),
87
+ })
88
+ .noUnknown();
89
+
90
+ const createChangePasswordSchema = (config) =>
91
+ yup
92
+ .object({
93
+ password: yup
94
+ .string()
95
+ .required()
96
+ .test(function (value) {
97
+ if (!value) return true;
98
+ const isValid = new TextEncoder().encode(value).length <= 72;
99
+ if (!isValid) {
100
+ return this.createError({ message: 'Password must be less than 73 bytes' });
101
+ }
102
+ return true;
103
+ })
104
+ .test(async function (value) {
105
+ if (typeof config?.validatePassword === 'function') {
106
+ try {
107
+ const isValid = await config.validatePassword(value);
108
+ if (!isValid) {
109
+ return this.createError({ message: 'Password validation failed.' });
110
+ }
111
+ } catch (error) {
112
+ return this.createError({ message: error.message || 'An error occurred.' });
113
+ }
114
+ }
115
+ return true;
116
+ }),
117
+ passwordConfirmation: yup
118
+ .string()
119
+ .required()
120
+ .oneOf([yup.ref('password')], 'Passwords do not match'),
121
+ currentPassword: yup.string().required(),
122
+ })
123
+ .noUnknown();
48
124
 
49
125
  module.exports = {
50
126
  validateCallbackBody: validateYupSchema(callbackSchema),
51
- validateRegisterBody: validateYupSchema(registerSchema),
127
+ validateRegisterBody: (payload, config) =>
128
+ validateYupSchema(createRegisterSchema(config))(payload),
52
129
  validateSendEmailConfirmationBody: validateYupSchema(sendEmailConfirmationSchema),
53
130
  validateEmailConfirmationBody: validateYupSchema(validateEmailConfirmationSchema),
54
131
  validateForgotPasswordBody: validateYupSchema(forgotPasswordSchema),
55
- validateResetPasswordBody: validateYupSchema(resetPasswordSchema),
56
- validateChangePasswordBody: validateYupSchema(changePasswordSchema),
132
+ validateResetPasswordBody: (payload, config) =>
133
+ validateYupSchema(createResetPasswordSchema(config))(payload),
134
+ validateChangePasswordBody: (payload, config) =>
135
+ validateYupSchema(createChangePasswordSchema(config))(payload),
57
136
  };
@@ -10,6 +10,7 @@ const typesFactories = [
10
10
  require('./create-role-payload'),
11
11
  require('./update-role-payload'),
12
12
  require('./delete-role-payload'),
13
+ require('./user-input'),
13
14
  ];
14
15
 
15
16
  /**
@@ -6,6 +6,7 @@ module.exports = ({ nexus }) => {
6
6
 
7
7
  definition(t) {
8
8
  t.nonNull.id('id');
9
+ t.nonNull.id('documentId');
9
10
  t.nonNull.string('username');
10
11
  t.string('email');
11
12
  t.boolean('confirmed');
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ const usersPermissionsUserUID = 'plugin::users-permissions.user';
4
+
5
+ module.exports = ({ nexus, strapi }) => {
6
+ const { getContentTypeInputName } = strapi.plugin('graphql').service('utils').naming;
7
+
8
+ const userContentType = strapi.getModel(usersPermissionsUserUID);
9
+ const userInputName = getContentTypeInputName(userContentType);
10
+
11
+ return nexus.extendInputType({
12
+ type: userInputName,
13
+
14
+ definition(t) {
15
+ // Manually add the private password field back to the data
16
+ // input type as it is used for CRUD operations on users
17
+ t.string('password');
18
+ },
19
+ });
20
+ };
@@ -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
- const ratelimit = require('koa2-ratelimit').RateLimit;
7
-
8
- const message = [
9
- {
10
- messages: [
11
- {
12
- id: 'Auth.form.error.ratelimit',
13
- message: 'Too many attempts, please try again in a minute.',
14
- },
15
- ],
16
- },
17
- ];
18
-
19
- return ratelimit.middleware({
20
- interval: 1 * 60 * 1000,
21
- max: 5,
22
- prefixKey: `${ctx.request.path}:${ctx.request.ip}`,
23
- message,
24
- ...strapi.config.get('plugin.users-permissions.ratelimit'),
25
- ...config,
26
- })(ctx, next);
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
  };
@@ -7,7 +7,7 @@ const authStrategy = require('./strategies/users-permissions');
7
7
  const sanitizers = require('./utils/sanitize/sanitizers');
8
8
 
9
9
  module.exports = ({ strapi }) => {
10
- strapi.container.get('auth').register('content-api', authStrategy);
10
+ strapi.get('auth').register('content-api', authStrategy);
11
11
  strapi.sanitizers.add('content-api.output', sanitizers.defaultSanitizeOutput);
12
12
 
13
13
  if (strapi.plugin('graphql')) {
@@ -15,7 +15,7 @@ module.exports = ({ strapi }) => {
15
15
  }
16
16
 
17
17
  if (strapi.plugin('documentation')) {
18
- const specPath = path.join(__dirname, '../documentation/content-api.yaml');
18
+ const specPath = path.join(__dirname, '../../documentation/content-api.yaml');
19
19
  const spec = fs.readFileSync(specPath, 'utf8');
20
20
 
21
21
  strapi
@@ -29,10 +29,10 @@ module.exports = ({ strapi }) => ({
29
29
  },
30
30
 
31
31
  issue(payload, jwtOptions = {}) {
32
- _.defaults(jwtOptions, strapi.config.get('plugin.users-permissions.jwt'));
32
+ _.defaults(jwtOptions, strapi.config.get('plugin::users-permissions.jwt'));
33
33
  return jwt.sign(
34
34
  _.clone(payload.toJSON ? payload.toJSON() : payload),
35
- strapi.config.get('plugin.users-permissions.jwtSecret'),
35
+ strapi.config.get('plugin::users-permissions.jwtSecret'),
36
36
  jwtOptions
37
37
  );
38
38
  },
@@ -41,7 +41,7 @@ module.exports = ({ strapi }) => ({
41
41
  return new Promise((resolve, reject) => {
42
42
  jwt.verify(
43
43
  token,
44
- strapi.config.get('plugin.users-permissions.jwtSecret'),
44
+ strapi.config.get('plugin::users-permissions.jwtSecret'),
45
45
  {},
46
46
  (err, tokenPayload = {}) => {
47
47
  if (err) {
@@ -11,11 +11,7 @@ module.exports = ({ strapi }) => ({
11
11
  * @return {object[]}
12
12
  */
13
13
  async findRolePermissions(roleID) {
14
- return strapi.entityService.load(
15
- 'plugin::users-permissions.role',
16
- { id: roleID },
17
- 'permissions'
18
- );
14
+ return strapi.db.query('plugin::users-permissions.role').load({ id: roleID }, 'permissions');
19
15
  },
20
16
 
21
17
  /**
@@ -24,8 +20,8 @@ module.exports = ({ strapi }) => ({
24
20
  * @return {object[]}
25
21
  */
26
22
  async findPublicPermissions() {
27
- return strapi.entityService.findMany('plugin::users-permissions.permission', {
28
- filters: PUBLIC_ROLE_FILTER,
23
+ return strapi.db.query('plugin::users-permissions.permission').findMany({
24
+ where: PUBLIC_ROLE_FILTER,
29
25
  });
30
26
  },
31
27