@strapi/plugin-users-permissions 4.0.0-next.6 → 4.0.0

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 (196) hide show
  1. package/admin/src/components/BoundRoute/getMethodColor.js +41 -0
  2. package/admin/src/components/BoundRoute/index.js +40 -24
  3. package/admin/src/components/FormModal/Input/index.js +121 -0
  4. package/admin/src/components/FormModal/index.js +123 -0
  5. package/admin/src/components/Permissions/PermissionRow/CheckboxWrapper.js +19 -26
  6. package/admin/src/components/Permissions/PermissionRow/SubCategory.js +118 -0
  7. package/admin/src/components/Permissions/PermissionRow/index.js +9 -48
  8. package/admin/src/components/Permissions/index.js +36 -24
  9. package/admin/src/components/Permissions/init.js +1 -6
  10. package/admin/src/components/Policies/index.js +46 -47
  11. package/admin/src/components/UsersPermissions/index.js +29 -26
  12. package/admin/src/components/UsersPermissions/init.js +1 -2
  13. package/admin/src/hooks/useFetchRole/index.js +17 -7
  14. package/admin/src/hooks/useForm/index.js +3 -29
  15. package/admin/src/hooks/useForm/reducer.js +2 -21
  16. package/admin/src/hooks/usePlugins/index.js +12 -21
  17. package/admin/src/hooks/usePlugins/reducer.js +0 -3
  18. package/admin/src/index.js +29 -34
  19. package/admin/src/pages/AdvancedSettings/index.js +210 -193
  20. package/admin/src/pages/AdvancedSettings/utils/api.js +13 -0
  21. package/admin/src/pages/AdvancedSettings/utils/layout.js +96 -0
  22. package/admin/src/pages/AdvancedSettings/utils/schema.js +22 -0
  23. package/admin/src/pages/EmailTemplates/components/EmailForm.js +173 -0
  24. package/admin/src/pages/EmailTemplates/components/EmailTable.js +116 -0
  25. package/admin/src/pages/EmailTemplates/index.js +125 -198
  26. package/admin/src/pages/EmailTemplates/utils/api.js +13 -0
  27. package/admin/src/pages/Providers/index.js +208 -216
  28. package/admin/src/pages/Providers/utils/api.js +21 -0
  29. package/admin/src/pages/Providers/utils/forms.js +168 -126
  30. package/admin/src/pages/Roles/CreatePage/index.js +155 -147
  31. package/admin/src/pages/Roles/EditPage/index.js +162 -134
  32. package/admin/src/pages/Roles/ListPage/components/TableBody.js +96 -0
  33. package/admin/src/pages/Roles/ListPage/index.js +176 -156
  34. package/admin/src/pages/Roles/ListPage/utils/api.js +28 -0
  35. package/admin/src/pages/Roles/index.js +14 -8
  36. package/admin/src/permissions.js +12 -14
  37. package/admin/src/translations/ar.json +0 -8
  38. package/admin/src/translations/cs.json +0 -8
  39. package/admin/src/translations/de.json +0 -8
  40. package/admin/src/translations/dk.json +0 -8
  41. package/admin/src/translations/en.json +33 -12
  42. package/admin/src/translations/es.json +0 -8
  43. package/admin/src/translations/fr.json +0 -8
  44. package/admin/src/translations/id.json +0 -8
  45. package/admin/src/translations/it.json +0 -8
  46. package/admin/src/translations/ja.json +0 -8
  47. package/admin/src/translations/ko.json +0 -8
  48. package/admin/src/translations/ms.json +0 -8
  49. package/admin/src/translations/nl.json +0 -8
  50. package/admin/src/translations/pl.json +0 -8
  51. package/admin/src/translations/pt-BR.json +0 -8
  52. package/admin/src/translations/pt.json +0 -8
  53. package/admin/src/translations/ru.json +0 -8
  54. package/admin/src/translations/sk.json +0 -8
  55. package/admin/src/translations/sv.json +0 -8
  56. package/admin/src/translations/th.json +0 -8
  57. package/admin/src/translations/tr.json +0 -8
  58. package/admin/src/translations/uk.json +0 -8
  59. package/admin/src/translations/vi.json +0 -8
  60. package/admin/src/translations/zh-Hans.json +5 -14
  61. package/admin/src/translations/zh.json +0 -8
  62. package/admin/src/utils/axiosInstance.js +36 -0
  63. package/admin/src/utils/formatPluginName.js +26 -0
  64. package/admin/src/utils/index.js +1 -0
  65. package/documentation/1.0.0/overrides/users-permissions-Role.json +6 -6
  66. package/documentation/1.0.0/overrides/users-permissions-User.json +7 -7
  67. package/package.json +30 -29
  68. package/{config/functions/bootstrap.js → server/bootstrap/index.js} +26 -33
  69. package/{config → server/bootstrap}/users-permissions-actions.js +0 -0
  70. package/server/config.js +23 -0
  71. package/server/content-types/index.js +11 -0
  72. package/server/content-types/permission/index.js +34 -0
  73. package/server/content-types/role/index.js +51 -0
  74. package/server/content-types/user/index.js +72 -0
  75. package/{models/User.config.js → server/content-types/user/schema-config.js} +0 -0
  76. package/server/controllers/auth.js +440 -0
  77. package/server/controllers/content-manager-user.js +183 -0
  78. package/server/controllers/index.js +17 -0
  79. package/server/controllers/permissions.js +26 -0
  80. package/server/controllers/role.js +77 -0
  81. package/server/controllers/settings.js +85 -0
  82. package/server/controllers/user.js +191 -0
  83. package/server/controllers/validation/auth.js +29 -0
  84. package/{controllers → server/controllers}/validation/email-template.js +0 -0
  85. package/server/controllers/validation/user.js +38 -0
  86. package/server/graphql/index.js +44 -0
  87. package/server/graphql/mutations/auth/email-confirmation.js +39 -0
  88. package/server/graphql/mutations/auth/forgot-password.js +38 -0
  89. package/server/graphql/mutations/auth/login.js +38 -0
  90. package/server/graphql/mutations/auth/register.js +39 -0
  91. package/server/graphql/mutations/auth/reset-password.js +41 -0
  92. package/server/graphql/mutations/crud/role/create-role.js +37 -0
  93. package/server/graphql/mutations/crud/role/delete-role.js +28 -0
  94. package/server/graphql/mutations/crud/role/update-role.js +38 -0
  95. package/server/graphql/mutations/crud/user/create-user.js +48 -0
  96. package/server/graphql/mutations/crud/user/delete-user.js +42 -0
  97. package/server/graphql/mutations/crud/user/update-user.js +49 -0
  98. package/server/graphql/mutations/index.js +42 -0
  99. package/server/graphql/queries/index.js +13 -0
  100. package/server/graphql/queries/me.js +17 -0
  101. package/server/graphql/resolvers-configs.js +37 -0
  102. package/server/graphql/types/create-role-payload.js +11 -0
  103. package/server/graphql/types/delete-role-payload.js +11 -0
  104. package/server/graphql/types/index.js +21 -0
  105. package/server/graphql/types/login-input.js +13 -0
  106. package/server/graphql/types/login-payload.js +12 -0
  107. package/server/graphql/types/me-role.js +14 -0
  108. package/server/graphql/types/me.js +16 -0
  109. package/server/graphql/types/password-payload.js +11 -0
  110. package/server/graphql/types/register-input.js +13 -0
  111. package/server/graphql/types/update-role-payload.js +11 -0
  112. package/server/graphql/utils.js +27 -0
  113. package/server/index.js +21 -0
  114. package/server/middlewares/index.js +7 -0
  115. package/{config/policies → server/middlewares}/rateLimit.js +4 -8
  116. package/server/register.js +11 -0
  117. package/server/routes/admin/index.js +10 -0
  118. package/server/routes/admin/permissions.js +20 -0
  119. package/server/routes/admin/role.js +79 -0
  120. package/server/routes/admin/settings.js +95 -0
  121. package/server/routes/content-api/auth.js +73 -0
  122. package/server/routes/content-api/index.js +11 -0
  123. package/server/routes/content-api/permissions.js +9 -0
  124. package/server/routes/content-api/role.js +29 -0
  125. package/server/routes/content-api/user.js +61 -0
  126. package/server/routes/index.js +6 -0
  127. package/server/services/index.js +15 -0
  128. package/server/services/jwt.js +55 -0
  129. package/server/services/providers.js +599 -0
  130. package/server/services/role.js +177 -0
  131. package/{services → server/services}/user.js +32 -35
  132. package/server/services/users-permissions.js +233 -0
  133. package/server/strategies/users-permissions.js +123 -0
  134. package/{utils → server/utils}/index.d.ts +6 -1
  135. package/server/utils/index.js +9 -0
  136. package/strapi-admin.js +3 -0
  137. package/strapi-server.js +3 -0
  138. package/admin/src/assets/images/logo.svg +0 -1
  139. package/admin/src/components/BaselineAlignement/index.js +0 -33
  140. package/admin/src/components/Bloc/index.js +0 -10
  141. package/admin/src/components/BoundRoute/Components.js +0 -78
  142. package/admin/src/components/ContainerFluid/index.js +0 -13
  143. package/admin/src/components/FormBloc/index.js +0 -61
  144. package/admin/src/components/IntlInput/index.js +0 -38
  145. package/admin/src/components/ListBaselineAlignment/index.js +0 -8
  146. package/admin/src/components/ListRow/Components.js +0 -74
  147. package/admin/src/components/ListRow/index.js +0 -35
  148. package/admin/src/components/ModalForm/Wrapper.js +0 -12
  149. package/admin/src/components/ModalForm/index.js +0 -59
  150. package/admin/src/components/Permissions/ListWrapper.js +0 -9
  151. package/admin/src/components/Permissions/PermissionRow/BaselineAlignment.js +0 -7
  152. package/admin/src/components/Permissions/PermissionRow/RowStyle.js +0 -28
  153. package/admin/src/components/Permissions/PermissionRow/SubCategory/ConditionsButtonWrapper.js +0 -13
  154. package/admin/src/components/Permissions/PermissionRow/SubCategory/PolicyWrapper.js +0 -8
  155. package/admin/src/components/Permissions/PermissionRow/SubCategory/SubCategoryWrapper.js +0 -26
  156. package/admin/src/components/Permissions/PermissionRow/SubCategory/index.js +0 -116
  157. package/admin/src/components/Policies/Components.js +0 -26
  158. package/admin/src/components/PrefixedIcon/index.js +0 -27
  159. package/admin/src/components/Roles/EmptyRole/BaselineAlignment.js +0 -7
  160. package/admin/src/components/Roles/EmptyRole/index.js +0 -27
  161. package/admin/src/components/Roles/RoleListWrapper/index.js +0 -17
  162. package/admin/src/components/Roles/RoleRow/RoleDescription.js +0 -9
  163. package/admin/src/components/Roles/RoleRow/index.js +0 -45
  164. package/admin/src/components/Roles/index.js +0 -3
  165. package/admin/src/components/SizedInput/index.js +0 -24
  166. package/admin/src/pages/AdvancedSettings/reducer.js +0 -65
  167. package/admin/src/pages/AdvancedSettings/utils/form.js +0 -52
  168. package/admin/src/pages/EmailTemplates/CustomTextInput.js +0 -105
  169. package/admin/src/pages/EmailTemplates/Wrapper.js +0 -36
  170. package/admin/src/pages/EmailTemplates/reducer.js +0 -58
  171. package/admin/src/pages/EmailTemplates/utils/forms.js +0 -81
  172. package/admin/src/pages/Roles/ListPage/BaselineAlignment.js +0 -8
  173. package/config/layout.js +0 -10
  174. package/config/policies/isAuthenticated.js +0 -9
  175. package/config/policies/permissions.js +0 -94
  176. package/config/request.json +0 -6
  177. package/config/routes.json +0 -381
  178. package/config/schema.graphql.js +0 -284
  179. package/config/security.json +0 -5
  180. package/controllers/auth.js +0 -596
  181. package/controllers/user/admin.js +0 -230
  182. package/controllers/user/api.js +0 -174
  183. package/controllers/user.js +0 -117
  184. package/controllers/users-permissions.js +0 -271
  185. package/middlewares/users-permissions/defaults.json +0 -5
  186. package/middlewares/users-permissions/index.js +0 -40
  187. package/models/Permission.js +0 -7
  188. package/models/Permission.settings.json +0 -45
  189. package/models/Role.js +0 -7
  190. package/models/Role.settings.json +0 -43
  191. package/models/User.js +0 -7
  192. package/models/User.settings.json +0 -63
  193. package/services/jwt.js +0 -65
  194. package/services/providers.js +0 -598
  195. package/services/users-permissions.js +0 -429
  196. package/utils/index.js +0 -11
@@ -0,0 +1,440 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Auth.js controller
5
+ *
6
+ * @description: A set of functions called "actions" for managing `Auth`.
7
+ */
8
+
9
+ /* eslint-disable no-useless-escape */
10
+ const crypto = require('crypto');
11
+ const _ = require('lodash');
12
+ const utils = require('@strapi/utils');
13
+ const { getService } = require('../utils');
14
+ const {
15
+ validateCallbackBody,
16
+ validateRegisterBody,
17
+ validateSendEmailConfirmationBody,
18
+ } = require('./validation/auth');
19
+
20
+ const { sanitize } = utils;
21
+ const { ApplicationError, ValidationError } = utils.errors;
22
+
23
+ const emailRegExp = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
24
+
25
+ const sanitizeUser = (user, ctx) => {
26
+ const { auth } = ctx.state;
27
+ const userSchema = strapi.getModel('plugin::users-permissions.user');
28
+
29
+ return sanitize.contentAPI.output(user, userSchema, { auth });
30
+ };
31
+
32
+ module.exports = {
33
+ async callback(ctx) {
34
+ const provider = ctx.params.provider || 'local';
35
+ const params = ctx.request.body;
36
+
37
+ const store = await strapi.store({ type: 'plugin', name: 'users-permissions' });
38
+
39
+ if (provider === 'local') {
40
+ if (!_.get(await store.get({ key: 'grant' }), 'email.enabled')) {
41
+ throw new ApplicationError('This provider is disabled');
42
+ }
43
+
44
+ await validateCallbackBody(params);
45
+
46
+ const query = { provider };
47
+
48
+ // Check if the provided identifier is an email or not.
49
+ const isEmail = emailRegExp.test(params.identifier);
50
+
51
+ // Set the identifier to the appropriate query field.
52
+ if (isEmail) {
53
+ query.email = params.identifier.toLowerCase();
54
+ } else {
55
+ query.username = params.identifier;
56
+ }
57
+
58
+ // Check if the user exists.
59
+ const user = await strapi.query('plugin::users-permissions.user').findOne({ where: query });
60
+
61
+ if (!user) {
62
+ throw new ValidationError('Invalid identifier or password');
63
+ }
64
+
65
+ if (
66
+ _.get(await store.get({ key: 'advanced' }), 'email_confirmation') &&
67
+ user.confirmed !== true
68
+ ) {
69
+ throw new ApplicationError('Your account email is not confirmed');
70
+ }
71
+
72
+ if (user.blocked === true) {
73
+ throw new ApplicationError('Your account has been blocked by an administrator');
74
+ }
75
+
76
+ // The user never authenticated with the `local` provider.
77
+ if (!user.password) {
78
+ throw new ApplicationError(
79
+ 'This user never set a local password, please login with the provider used during account creation'
80
+ );
81
+ }
82
+
83
+ const validPassword = await getService('user').validatePassword(
84
+ params.password,
85
+ user.password
86
+ );
87
+
88
+ if (!validPassword) {
89
+ throw new ValidationError('Invalid identifier or password');
90
+ } else {
91
+ ctx.send({
92
+ jwt: getService('jwt').issue({
93
+ id: user.id,
94
+ }),
95
+ user: await sanitizeUser(user, ctx),
96
+ });
97
+ }
98
+ } else {
99
+ if (!_.get(await store.get({ key: 'grant' }), [provider, 'enabled'])) {
100
+ throw new ApplicationError('This provider is disabled');
101
+ }
102
+
103
+ // Connect the user with the third-party provider.
104
+ let user;
105
+ let error;
106
+ try {
107
+ [user, error] = await getService('providers').connect(provider, ctx.query);
108
+ } catch ([user, error]) {
109
+ throw new ApplicationError(error.message);
110
+ }
111
+
112
+ if (!user) {
113
+ throw new ApplicationError(error.message);
114
+ }
115
+
116
+ ctx.send({
117
+ jwt: getService('jwt').issue({ id: user.id }),
118
+ user: await sanitizeUser(user, ctx),
119
+ });
120
+ }
121
+ },
122
+
123
+ async resetPassword(ctx) {
124
+ const params = _.assign({}, ctx.request.body, ctx.params);
125
+
126
+ if (
127
+ params.password &&
128
+ params.passwordConfirmation &&
129
+ params.password === params.passwordConfirmation &&
130
+ params.code
131
+ ) {
132
+ const user = await strapi
133
+ .query('plugin::users-permissions.user')
134
+ .findOne({ where: { resetPasswordToken: `${params.code}` } });
135
+
136
+ if (!user) {
137
+ throw new ValidationError('Incorrect code provided');
138
+ }
139
+
140
+ const password = await getService('user').hashPassword({ password: params.password });
141
+
142
+ // Update the user.
143
+ await strapi
144
+ .query('plugin::users-permissions.user')
145
+ .update({ where: { id: user.id }, data: { resetPasswordToken: null, password } });
146
+
147
+ ctx.send({
148
+ jwt: getService('jwt').issue({ id: user.id }),
149
+ user: await sanitizeUser(user, ctx),
150
+ });
151
+ } else if (
152
+ params.password &&
153
+ params.passwordConfirmation &&
154
+ params.password !== params.passwordConfirmation
155
+ ) {
156
+ throw new ValidationError('Passwords do not match');
157
+ } else {
158
+ throw new ValidationError('Incorrect params provided');
159
+ }
160
+ },
161
+
162
+ async connect(ctx, next) {
163
+ const grant = require('grant-koa');
164
+
165
+ const providers = await strapi
166
+ .store({ type: 'plugin', name: 'users-permissions', key: 'grant' })
167
+ .get();
168
+
169
+ const apiPrefix = strapi.config.get('api.rest.prefix');
170
+ const grantConfig = {
171
+ defaults: {
172
+ prefix: `${apiPrefix}/connect`,
173
+ },
174
+ ...providers,
175
+ };
176
+
177
+ const [requestPath] = ctx.request.url.split('?');
178
+ const provider = requestPath.split('/connect/')[1].split('/')[0];
179
+
180
+ if (!_.get(grantConfig[provider], 'enabled')) {
181
+ throw new ApplicationError('This provider is disabled');
182
+ }
183
+
184
+ if (!strapi.config.server.url.startsWith('http')) {
185
+ strapi.log.warn(
186
+ 'You are using a third party provider for login. Make sure to set an absolute url in config/server.js. More info here: https://docs.strapi.io/developer-docs/latest/plugins/users-permissions.html#setting-up-the-server-url'
187
+ );
188
+ }
189
+
190
+ // Ability to pass OAuth callback dynamically
191
+ grantConfig[provider].callback = _.get(ctx, 'query.callback') || grantConfig[provider].callback;
192
+ grantConfig[provider].redirect_uri = getService('providers').buildRedirectUri(provider);
193
+
194
+ return grant(grantConfig)(ctx, next);
195
+ },
196
+
197
+ async forgotPassword(ctx) {
198
+ let { email } = ctx.request.body;
199
+
200
+ // Check if the provided email is valid or not.
201
+ const isEmail = emailRegExp.test(email);
202
+
203
+ if (isEmail) {
204
+ email = email.toLowerCase();
205
+ } else {
206
+ throw new ValidationError('Please provide a valid email address');
207
+ }
208
+
209
+ const pluginStore = await strapi.store({ type: 'plugin', name: 'users-permissions' });
210
+
211
+ // Find the user by email.
212
+ const user = await strapi
213
+ .query('plugin::users-permissions.user')
214
+ .findOne({ where: { email: email.toLowerCase() } });
215
+
216
+ // User not found.
217
+ if (!user) {
218
+ throw new ApplicationError('This email does not exist');
219
+ }
220
+
221
+ // User blocked
222
+ if (user.blocked) {
223
+ throw new ApplicationError('This user is disabled');
224
+ }
225
+
226
+ // Generate random token.
227
+ const resetPasswordToken = crypto.randomBytes(64).toString('hex');
228
+
229
+ const settings = await pluginStore.get({ key: 'email' }).then(storeEmail => {
230
+ try {
231
+ return storeEmail['reset_password'].options;
232
+ } catch (error) {
233
+ return {};
234
+ }
235
+ });
236
+
237
+ const advanced = await pluginStore.get({
238
+ key: 'advanced',
239
+ });
240
+
241
+ const userInfo = await sanitizeUser(user, ctx);
242
+
243
+ settings.message = await getService('users-permissions').template(settings.message, {
244
+ URL: advanced.email_reset_password,
245
+ USER: userInfo,
246
+ TOKEN: resetPasswordToken,
247
+ });
248
+
249
+ settings.object = await getService('users-permissions').template(settings.object, {
250
+ USER: userInfo,
251
+ });
252
+
253
+ try {
254
+ // Send an email to the user.
255
+ await strapi
256
+ .plugin('email')
257
+ .service('email')
258
+ .send({
259
+ to: user.email,
260
+ from:
261
+ settings.from.email || settings.from.name
262
+ ? `${settings.from.name} <${settings.from.email}>`
263
+ : undefined,
264
+ replyTo: settings.response_email,
265
+ subject: settings.object,
266
+ text: settings.message,
267
+ html: settings.message,
268
+ });
269
+ } catch (err) {
270
+ throw new ApplicationError(err.message);
271
+ }
272
+
273
+ // Update the user.
274
+ await strapi
275
+ .query('plugin::users-permissions.user')
276
+ .update({ where: { id: user.id }, data: { resetPasswordToken } });
277
+
278
+ ctx.send({ ok: true });
279
+ },
280
+
281
+ async register(ctx) {
282
+ const pluginStore = await strapi.store({ type: 'plugin', name: 'users-permissions' });
283
+
284
+ const settings = await pluginStore.get({
285
+ key: 'advanced',
286
+ });
287
+
288
+ if (!settings.allow_register) {
289
+ throw new ApplicationError('Register action is currently disabled');
290
+ }
291
+
292
+ const params = {
293
+ ..._.omit(ctx.request.body, ['confirmed', 'confirmationToken', 'resetPasswordToken']),
294
+ provider: 'local',
295
+ };
296
+
297
+ await validateRegisterBody(params);
298
+
299
+ // Throw an error if the password selected by the user
300
+ // contains more than three times the symbol '$'.
301
+ if (getService('user').isHashed(params.password)) {
302
+ throw new ValidationError(
303
+ 'Your password cannot contain more than three times the symbol `$`'
304
+ );
305
+ }
306
+
307
+ const role = await strapi
308
+ .query('plugin::users-permissions.role')
309
+ .findOne({ where: { type: settings.default_role } });
310
+
311
+ if (!role) {
312
+ throw new ApplicationError('Impossible to find the default role');
313
+ }
314
+
315
+ // Check if the provided email is valid or not.
316
+ const isEmail = emailRegExp.test(params.email);
317
+
318
+ if (isEmail) {
319
+ params.email = params.email.toLowerCase();
320
+ } else {
321
+ throw new ValidationError('Please provide a valid email address');
322
+ }
323
+
324
+ params.role = role.id;
325
+ params.password = await getService('user').hashPassword(params);
326
+
327
+ const user = await strapi.query('plugin::users-permissions.user').findOne({
328
+ where: { email: params.email },
329
+ });
330
+
331
+ if (user && user.provider === params.provider) {
332
+ throw new ApplicationError('Email is already taken');
333
+ }
334
+
335
+ if (user && user.provider !== params.provider && settings.unique_email) {
336
+ throw new ApplicationError('Email is already taken');
337
+ }
338
+
339
+ try {
340
+ if (!settings.email_confirmation) {
341
+ params.confirmed = true;
342
+ }
343
+
344
+ const user = await strapi.query('plugin::users-permissions.user').create({ data: params });
345
+
346
+ const sanitizedUser = await sanitizeUser(user, ctx);
347
+
348
+ if (settings.email_confirmation) {
349
+ try {
350
+ await getService('user').sendConfirmationEmail(sanitizedUser);
351
+ } catch (err) {
352
+ throw new ApplicationError(err.message);
353
+ }
354
+
355
+ return ctx.send({ user: sanitizedUser });
356
+ }
357
+
358
+ const jwt = getService('jwt').issue(_.pick(user, ['id']));
359
+
360
+ return ctx.send({
361
+ jwt,
362
+ user: sanitizedUser,
363
+ });
364
+ } catch (err) {
365
+ if (_.includes(err.message, 'username')) {
366
+ throw new ApplicationError('Username already taken');
367
+ } else {
368
+ throw new ApplicationError('Email already taken');
369
+ }
370
+ }
371
+ },
372
+
373
+ async emailConfirmation(ctx, next, returnUser) {
374
+ const { confirmation: confirmationToken } = ctx.query;
375
+
376
+ const userService = getService('user');
377
+ const jwtService = getService('jwt');
378
+
379
+ if (_.isEmpty(confirmationToken)) {
380
+ throw new ValidationError('token.invalid');
381
+ }
382
+
383
+ const user = await userService.fetch({ confirmationToken }, []);
384
+
385
+ if (!user) {
386
+ throw new ValidationError('token.invalid');
387
+ }
388
+
389
+ await userService.edit({ id: user.id }, { confirmed: true, confirmationToken: null });
390
+
391
+ if (returnUser) {
392
+ ctx.send({
393
+ jwt: jwtService.issue({ id: user.id }),
394
+ user: await sanitizeUser(user, ctx),
395
+ });
396
+ } else {
397
+ const settings = await strapi
398
+ .store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
399
+ .get();
400
+
401
+ ctx.redirect(settings.email_confirmation_redirection || '/');
402
+ }
403
+ },
404
+
405
+ async sendEmailConfirmation(ctx) {
406
+ const params = _.assign(ctx.request.body);
407
+
408
+ await validateSendEmailConfirmationBody(params);
409
+
410
+ const isEmail = emailRegExp.test(params.email);
411
+
412
+ if (isEmail) {
413
+ params.email = params.email.toLowerCase();
414
+ } else {
415
+ throw new ValidationError('wrong.email');
416
+ }
417
+
418
+ const user = await strapi.query('plugin::users-permissions.user').findOne({
419
+ where: { email: params.email },
420
+ });
421
+
422
+ if (user.confirmed) {
423
+ throw new ApplicationError('already.confirmed');
424
+ }
425
+
426
+ if (user.blocked) {
427
+ throw new ApplicationError('blocked.user');
428
+ }
429
+
430
+ try {
431
+ await getService('user').sendConfirmationEmail(user);
432
+ ctx.send({
433
+ email: user.email,
434
+ sent: true,
435
+ });
436
+ } catch (err) {
437
+ throw new ApplicationError(err.message);
438
+ }
439
+ },
440
+ };
@@ -0,0 +1,183 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+ const { contentTypes: contentTypesUtils } = require('@strapi/utils');
5
+ const {
6
+ ApplicationError,
7
+ ValidationError,
8
+ NotFoundError,
9
+ ForbiddenError,
10
+ } = require('@strapi/utils').errors;
11
+ const { validateCreateUserBody, validateUpdateUserBody } = require('./validation/user');
12
+
13
+ const { UPDATED_BY_ATTRIBUTE, CREATED_BY_ATTRIBUTE } = contentTypesUtils.constants;
14
+
15
+ const userModel = 'plugin::users-permissions.user';
16
+ const ACTIONS = {
17
+ read: 'plugin::content-manager.explorer.read',
18
+ create: 'plugin::content-manager.explorer.create',
19
+ edit: 'plugin::content-manager.explorer.update',
20
+ delete: 'plugin::content-manager.explorer.delete',
21
+ };
22
+
23
+ const findEntityAndCheckPermissions = async (ability, action, model, id) => {
24
+ const entity = await strapi.query(userModel).findOne({
25
+ where: { id },
26
+ populate: [`${CREATED_BY_ATTRIBUTE}.roles`],
27
+ });
28
+
29
+ if (_.isNil(entity)) {
30
+ throw new NotFoundError();
31
+ }
32
+
33
+ const pm = strapi.admin.services.permission.createPermissionsManager({ ability, action, model });
34
+
35
+ if (pm.ability.cannot(pm.action, pm.toSubject(entity))) {
36
+ throw new ForbiddenError();
37
+ }
38
+
39
+ const entityWithoutCreatorRoles = _.omit(entity, `${CREATED_BY_ATTRIBUTE}.roles`);
40
+
41
+ return { pm, entity: entityWithoutCreatorRoles };
42
+ };
43
+
44
+ module.exports = {
45
+ /**
46
+ * Create a/an user record.
47
+ * @return {Object}
48
+ */
49
+ async create(ctx) {
50
+ const { body } = ctx.request;
51
+ const { user: admin, userAbility } = ctx.state;
52
+
53
+ const { email, username } = body;
54
+
55
+ const pm = strapi.admin.services.permission.createPermissionsManager({
56
+ ability: userAbility,
57
+ action: ACTIONS.create,
58
+ model: userModel,
59
+ });
60
+
61
+ if (!pm.isAllowed) {
62
+ return ctx.forbidden();
63
+ }
64
+
65
+ const sanitizedBody = await pm.pickPermittedFieldsOf(body, { subject: userModel });
66
+
67
+ const advanced = await strapi
68
+ .store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
69
+ .get();
70
+
71
+ await validateCreateUserBody(ctx.request.body);
72
+
73
+ const userWithSameUsername = await strapi
74
+ .query('plugin::users-permissions.user')
75
+ .findOne({ where: { username } });
76
+
77
+ if (userWithSameUsername) {
78
+ throw new ApplicationError('Username already taken');
79
+ }
80
+
81
+ if (advanced.unique_email) {
82
+ const userWithSameEmail = await strapi
83
+ .query('plugin::users-permissions.user')
84
+ .findOne({ where: { email: email.toLowerCase() } });
85
+
86
+ if (userWithSameEmail) {
87
+ throw new ApplicationError('Email already taken');
88
+ }
89
+ }
90
+
91
+ const user = {
92
+ ...sanitizedBody,
93
+ provider: 'local',
94
+ [CREATED_BY_ATTRIBUTE]: admin.id,
95
+ [UPDATED_BY_ATTRIBUTE]: admin.id,
96
+ };
97
+
98
+ user.email = _.toLower(user.email);
99
+
100
+ if (!user.role) {
101
+ const defaultRole = await strapi
102
+ .query('plugin::users-permissions.role')
103
+ .findOne({ where: { type: advanced.default_role } });
104
+
105
+ user.role = defaultRole.id;
106
+ }
107
+
108
+ try {
109
+ const data = await strapi
110
+ .service('plugin::content-manager.entity-manager')
111
+ .create(user, userModel);
112
+ const sanitizedData = await pm.sanitizeOutput(data, { action: ACTIONS.read });
113
+
114
+ ctx.created(sanitizedData);
115
+ } catch (error) {
116
+ throw new ApplicationError(error.message);
117
+ }
118
+ },
119
+ /**
120
+ * Update a/an user record.
121
+ * @return {Object}
122
+ */
123
+
124
+ async update(ctx) {
125
+ const { id } = ctx.params;
126
+ const { body } = ctx.request;
127
+ const { user: admin, userAbility } = ctx.state;
128
+
129
+ const advancedConfigs = await strapi
130
+ .store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
131
+ .get();
132
+
133
+ const { email, username, password } = body;
134
+
135
+ let pm;
136
+ let user;
137
+
138
+ const { pm: permissionManager, entity } = await findEntityAndCheckPermissions(
139
+ userAbility,
140
+ ACTIONS.edit,
141
+ userModel,
142
+ id
143
+ );
144
+ pm = permissionManager;
145
+ user = entity;
146
+
147
+ await validateUpdateUserBody(ctx.request.body);
148
+
149
+ if (_.has(body, 'password') && !password && user.provider === 'local') {
150
+ throw new ValidationError('password.notNull');
151
+ }
152
+
153
+ if (_.has(body, 'username')) {
154
+ const userWithSameUsername = await strapi
155
+ .query('plugin::users-permissions.user')
156
+ .findOne({ where: { username } });
157
+
158
+ if (userWithSameUsername && userWithSameUsername.id != id) {
159
+ throw new ApplicationError('Username already taken');
160
+ }
161
+ }
162
+
163
+ if (_.has(body, 'email') && advancedConfigs.unique_email) {
164
+ const userWithSameEmail = await strapi
165
+ .query('plugin::users-permissions.user')
166
+ .findOne({ where: { email: _.toLower(email) } });
167
+
168
+ if (userWithSameEmail && userWithSameEmail.id != id) {
169
+ throw new ApplicationError('Email already taken');
170
+ }
171
+ body.email = _.toLower(body.email);
172
+ }
173
+
174
+ const sanitizedData = await pm.pickPermittedFieldsOf(body, { subject: pm.toSubject(user) });
175
+ const updateData = _.omit({ ...sanitizedData, updatedBy: admin.id }, 'createdBy');
176
+
177
+ const data = await strapi
178
+ .service('plugin::content-manager.entity-manager')
179
+ .update({ id }, updateData, userModel);
180
+
181
+ ctx.body = await pm.sanitizeOutput(data, { action: ACTIONS.read });
182
+ },
183
+ };
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ const auth = require('./auth');
4
+ const user = require('./user');
5
+ const role = require('./role');
6
+ const permissions = require('./permissions');
7
+ const settings = require('./settings');
8
+ const contentmanageruser = require('./content-manager-user');
9
+
10
+ module.exports = {
11
+ auth,
12
+ user,
13
+ role,
14
+ permissions,
15
+ settings,
16
+ contentmanageruser,
17
+ };
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+ const { getService } = require('../utils');
5
+
6
+ module.exports = {
7
+ async getPermissions(ctx) {
8
+ const permissions = await getService('users-permissions').getActions();
9
+
10
+ ctx.send({ permissions });
11
+ },
12
+
13
+ async getPolicies(ctx) {
14
+ const policies = _.keys(strapi.plugin('users-permissions').policies);
15
+
16
+ ctx.send({
17
+ policies: _.without(policies, 'permissions'),
18
+ });
19
+ },
20
+
21
+ async getRoutes(ctx) {
22
+ const routes = await getService('users-permissions').getRoutes();
23
+
24
+ ctx.send({ routes });
25
+ },
26
+ };