@strapi/plugin-users-permissions 4.0.0-next.9 → 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 (177) 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 +0 -8
  19. package/admin/src/pages/AdvancedSettings/index.js +203 -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 +117 -197
  26. package/admin/src/pages/EmailTemplates/utils/api.js +13 -0
  27. package/admin/src/pages/Providers/index.js +206 -221
  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/translations/ar.json +0 -8
  36. package/admin/src/translations/cs.json +0 -8
  37. package/admin/src/translations/de.json +0 -8
  38. package/admin/src/translations/dk.json +0 -8
  39. package/admin/src/translations/en.json +33 -12
  40. package/admin/src/translations/es.json +0 -8
  41. package/admin/src/translations/fr.json +0 -8
  42. package/admin/src/translations/id.json +0 -8
  43. package/admin/src/translations/it.json +0 -8
  44. package/admin/src/translations/ja.json +0 -8
  45. package/admin/src/translations/ko.json +0 -8
  46. package/admin/src/translations/ms.json +0 -8
  47. package/admin/src/translations/nl.json +0 -8
  48. package/admin/src/translations/pl.json +0 -8
  49. package/admin/src/translations/pt-BR.json +0 -8
  50. package/admin/src/translations/pt.json +0 -8
  51. package/admin/src/translations/ru.json +0 -8
  52. package/admin/src/translations/sk.json +0 -8
  53. package/admin/src/translations/sv.json +0 -8
  54. package/admin/src/translations/th.json +0 -8
  55. package/admin/src/translations/tr.json +0 -8
  56. package/admin/src/translations/uk.json +0 -8
  57. package/admin/src/translations/vi.json +0 -8
  58. package/admin/src/translations/zh-Hans.json +5 -14
  59. package/admin/src/translations/zh.json +0 -8
  60. package/admin/src/utils/axiosInstance.js +36 -0
  61. package/admin/src/utils/formatPluginName.js +26 -0
  62. package/admin/src/utils/index.js +1 -0
  63. package/documentation/1.0.0/overrides/users-permissions-Role.json +6 -6
  64. package/documentation/1.0.0/overrides/users-permissions-User.json +7 -7
  65. package/package.json +30 -31
  66. package/server/bootstrap/index.js +19 -21
  67. package/server/config.js +3 -3
  68. package/server/content-types/index.js +3 -3
  69. package/server/content-types/permission/index.js +30 -3
  70. package/server/content-types/role/index.js +47 -3
  71. package/server/content-types/user/index.js +65 -4
  72. package/server/controllers/auth.js +81 -244
  73. package/server/controllers/content-manager-user.js +183 -0
  74. package/server/controllers/index.js +12 -6
  75. package/server/controllers/permissions.js +26 -0
  76. package/server/controllers/role.js +77 -0
  77. package/server/controllers/settings.js +85 -0
  78. package/server/controllers/user.js +118 -44
  79. package/server/controllers/validation/auth.js +29 -0
  80. package/server/controllers/validation/user.js +38 -0
  81. package/server/graphql/index.js +44 -0
  82. package/server/graphql/mutations/auth/email-confirmation.js +39 -0
  83. package/server/graphql/mutations/auth/forgot-password.js +38 -0
  84. package/server/graphql/mutations/auth/login.js +38 -0
  85. package/server/graphql/mutations/auth/register.js +39 -0
  86. package/server/graphql/mutations/auth/reset-password.js +41 -0
  87. package/server/graphql/mutations/crud/role/create-role.js +37 -0
  88. package/server/graphql/mutations/crud/role/delete-role.js +28 -0
  89. package/server/graphql/mutations/crud/role/update-role.js +38 -0
  90. package/server/graphql/mutations/crud/user/create-user.js +48 -0
  91. package/server/graphql/mutations/crud/user/delete-user.js +42 -0
  92. package/server/graphql/mutations/crud/user/update-user.js +49 -0
  93. package/server/graphql/mutations/index.js +42 -0
  94. package/server/graphql/queries/index.js +13 -0
  95. package/server/graphql/queries/me.js +17 -0
  96. package/server/graphql/resolvers-configs.js +37 -0
  97. package/server/graphql/types/create-role-payload.js +11 -0
  98. package/server/graphql/types/delete-role-payload.js +11 -0
  99. package/server/graphql/types/index.js +21 -0
  100. package/server/graphql/types/login-input.js +13 -0
  101. package/server/graphql/types/login-payload.js +12 -0
  102. package/server/graphql/types/me-role.js +14 -0
  103. package/server/graphql/types/me.js +16 -0
  104. package/server/graphql/types/password-payload.js +11 -0
  105. package/server/graphql/types/register-input.js +13 -0
  106. package/server/graphql/types/update-role-payload.js +11 -0
  107. package/server/graphql/utils.js +27 -0
  108. package/server/index.js +21 -0
  109. package/server/middlewares/index.js +2 -2
  110. package/server/{policies → middlewares}/rateLimit.js +3 -7
  111. package/server/register.js +11 -0
  112. package/server/routes/admin/index.js +10 -0
  113. package/server/routes/admin/permissions.js +20 -0
  114. package/server/routes/admin/role.js +79 -0
  115. package/server/routes/admin/settings.js +95 -0
  116. package/server/routes/content-api/auth.js +73 -0
  117. package/server/routes/content-api/index.js +11 -0
  118. package/server/routes/content-api/permissions.js +9 -0
  119. package/server/routes/content-api/role.js +29 -0
  120. package/server/routes/content-api/user.js +61 -0
  121. package/server/routes/index.js +4 -428
  122. package/server/services/index.js +10 -8
  123. package/server/services/jwt.js +9 -17
  124. package/server/services/providers.js +32 -33
  125. package/server/services/role.js +177 -0
  126. package/server/services/user.js +9 -15
  127. package/server/services/users-permissions.js +140 -338
  128. package/server/strategies/users-permissions.js +123 -0
  129. package/server/utils/index.d.ts +2 -0
  130. package/strapi-admin.js +3 -0
  131. package/strapi-server.js +1 -19
  132. package/admin/src/assets/images/logo.svg +0 -1
  133. package/admin/src/components/BaselineAlignement/index.js +0 -33
  134. package/admin/src/components/Bloc/index.js +0 -10
  135. package/admin/src/components/BoundRoute/Components.js +0 -78
  136. package/admin/src/components/ContainerFluid/index.js +0 -13
  137. package/admin/src/components/FormBloc/index.js +0 -61
  138. package/admin/src/components/IntlInput/index.js +0 -38
  139. package/admin/src/components/ListBaselineAlignment/index.js +0 -8
  140. package/admin/src/components/ListRow/Components.js +0 -74
  141. package/admin/src/components/ListRow/index.js +0 -35
  142. package/admin/src/components/ModalForm/Wrapper.js +0 -12
  143. package/admin/src/components/ModalForm/index.js +0 -59
  144. package/admin/src/components/Permissions/ListWrapper.js +0 -9
  145. package/admin/src/components/Permissions/PermissionRow/BaselineAlignment.js +0 -7
  146. package/admin/src/components/Permissions/PermissionRow/RowStyle.js +0 -28
  147. package/admin/src/components/Permissions/PermissionRow/SubCategory/ConditionsButtonWrapper.js +0 -13
  148. package/admin/src/components/Permissions/PermissionRow/SubCategory/PolicyWrapper.js +0 -8
  149. package/admin/src/components/Permissions/PermissionRow/SubCategory/SubCategoryWrapper.js +0 -26
  150. package/admin/src/components/Permissions/PermissionRow/SubCategory/index.js +0 -116
  151. package/admin/src/components/Policies/Components.js +0 -26
  152. package/admin/src/components/PrefixedIcon/index.js +0 -27
  153. package/admin/src/components/Roles/EmptyRole/BaselineAlignment.js +0 -7
  154. package/admin/src/components/Roles/EmptyRole/index.js +0 -27
  155. package/admin/src/components/Roles/RoleListWrapper/index.js +0 -17
  156. package/admin/src/components/Roles/RoleRow/RoleDescription.js +0 -9
  157. package/admin/src/components/Roles/RoleRow/index.js +0 -45
  158. package/admin/src/components/Roles/index.js +0 -3
  159. package/admin/src/components/SizedInput/index.js +0 -24
  160. package/admin/src/pages/AdvancedSettings/reducer.js +0 -65
  161. package/admin/src/pages/AdvancedSettings/utils/form.js +0 -52
  162. package/admin/src/pages/EmailTemplates/CustomTextInput.js +0 -105
  163. package/admin/src/pages/EmailTemplates/Wrapper.js +0 -36
  164. package/admin/src/pages/EmailTemplates/reducer.js +0 -58
  165. package/admin/src/pages/EmailTemplates/utils/forms.js +0 -81
  166. package/admin/src/pages/Roles/ListPage/BaselineAlignment.js +0 -8
  167. package/server/content-types/permission/schema.json +0 -48
  168. package/server/content-types/role/schema.json +0 -46
  169. package/server/content-types/user/schema.json +0 -66
  170. package/server/controllers/user/admin.js +0 -230
  171. package/server/controllers/user/api.js +0 -174
  172. package/server/controllers/users-permissions.js +0 -271
  173. package/server/middlewares/users-permissions.js +0 -44
  174. package/server/policies/index.js +0 -11
  175. package/server/policies/isAuthenticated.js +0 -9
  176. package/server/policies/permissions.js +0 -94
  177. package/server/schema.graphql.js +0 -317
@@ -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
+ };
@@ -1,11 +1,17 @@
1
1
  'use strict';
2
2
 
3
- const authController = require('./auth');
4
- const userController = require('./user');
5
- const usersPermissionsController = require('./users-permissions');
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');
6
9
 
7
10
  module.exports = {
8
- auth: authController,
9
- user: userController,
10
- 'users-permissions': usersPermissionsController,
11
+ auth,
12
+ user,
13
+ role,
14
+ permissions,
15
+ settings,
16
+ contentmanageruser,
11
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
+ };
@@ -0,0 +1,77 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+ const { ApplicationError, ValidationError } = require('@strapi/utils').errors;
5
+ const { getService } = require('../utils');
6
+ const { validateDeleteRoleBody } = require('./validation/user');
7
+
8
+ module.exports = {
9
+ /**
10
+ * Default action.
11
+ *
12
+ * @return {Object}
13
+ */
14
+ async createRole(ctx) {
15
+ if (_.isEmpty(ctx.request.body)) {
16
+ throw new ValidationError('Request body cannot be empty');
17
+ }
18
+
19
+ await getService('role').createRole(ctx.request.body);
20
+
21
+ ctx.send({ ok: true });
22
+ },
23
+
24
+ async getRole(ctx) {
25
+ const { id } = ctx.params;
26
+
27
+ const role = await getService('role').getRole(id);
28
+
29
+ if (!role) {
30
+ return ctx.notFound();
31
+ }
32
+
33
+ ctx.send({ role });
34
+ },
35
+
36
+ async getRoles(ctx) {
37
+ const roles = await getService('role').getRoles();
38
+
39
+ ctx.send({ roles });
40
+ },
41
+
42
+ async updateRole(ctx) {
43
+ const roleID = ctx.params.role;
44
+
45
+ if (_.isEmpty(ctx.request.body)) {
46
+ throw new ValidationError('Request body cannot be empty');
47
+ }
48
+
49
+ await getService('role').updateRole(roleID, ctx.request.body);
50
+
51
+ ctx.send({ ok: true });
52
+ },
53
+
54
+ async deleteRole(ctx) {
55
+ const roleID = ctx.params.role;
56
+
57
+ if (!roleID) {
58
+ await validateDeleteRoleBody(ctx.params);
59
+ }
60
+
61
+ // Fetch public role.
62
+ const publicRole = await strapi
63
+ .query('plugin::users-permissions.role')
64
+ .findOne({ where: { type: 'public' } });
65
+
66
+ const publicRoleID = publicRole.id;
67
+
68
+ // Prevent from removing the public role.
69
+ if (roleID.toString() === publicRoleID.toString()) {
70
+ throw new ApplicationError('Cannot delete public role');
71
+ }
72
+
73
+ await getService('role').deleteRole(roleID, publicRoleID);
74
+
75
+ ctx.send({ ok: true });
76
+ },
77
+ };
@@ -0,0 +1,85 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+ const { ValidationError } = require('@strapi/utils').errors;
5
+ const { getService } = require('../utils');
6
+ const { isValidEmailTemplate } = require('./validation/email-template');
7
+
8
+ module.exports = {
9
+ async getEmailTemplate(ctx) {
10
+ ctx.send(await strapi.store({ type: 'plugin', name: 'users-permissions', key: 'email' }).get());
11
+ },
12
+
13
+ async updateEmailTemplate(ctx) {
14
+ if (_.isEmpty(ctx.request.body)) {
15
+ throw new ValidationError('Request body cannot be empty');
16
+ }
17
+
18
+ const emailTemplates = ctx.request.body['email-templates'];
19
+
20
+ for (let key in emailTemplates) {
21
+ const template = emailTemplates[key].options.message;
22
+
23
+ if (!isValidEmailTemplate(template)) {
24
+ throw new ValidationError('Invalid template');
25
+ }
26
+ }
27
+
28
+ await strapi
29
+ .store({ type: 'plugin', name: 'users-permissions', key: 'email' })
30
+ .set({ value: emailTemplates });
31
+
32
+ ctx.send({ ok: true });
33
+ },
34
+
35
+ async getAdvancedSettings(ctx) {
36
+ const settings = await strapi
37
+ .store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
38
+ .get();
39
+
40
+ const roles = await getService('role').getRoles();
41
+
42
+ ctx.send({ settings, roles });
43
+ },
44
+
45
+ async updateAdvancedSettings(ctx) {
46
+ if (_.isEmpty(ctx.request.body)) {
47
+ throw new ValidationError('Request body cannot be empty');
48
+ }
49
+
50
+ await strapi
51
+ .store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
52
+ .set({ value: ctx.request.body });
53
+
54
+ ctx.send({ ok: true });
55
+ },
56
+
57
+ async getProviders(ctx) {
58
+ const providers = await strapi
59
+ .store({ type: 'plugin', name: 'users-permissions', key: 'grant' })
60
+ .get();
61
+
62
+ for (const provider in providers) {
63
+ if (provider !== 'email') {
64
+ providers[provider].redirectUri = strapi
65
+ .plugin('users-permissions')
66
+ .service('providers')
67
+ .buildRedirectUri(provider);
68
+ }
69
+ }
70
+
71
+ ctx.send(providers);
72
+ },
73
+
74
+ async updateProviders(ctx) {
75
+ if (_.isEmpty(ctx.request.body)) {
76
+ throw new ValidationError('Request body cannot be empty');
77
+ }
78
+
79
+ await strapi
80
+ .store({ type: 'plugin', name: 'users-permissions', key: 'grant' })
81
+ .set({ value: ctx.request.body.providers });
82
+
83
+ ctx.send({ ok: true });
84
+ },
85
+ };
@@ -7,38 +7,127 @@
7
7
  */
8
8
 
9
9
  const _ = require('lodash');
10
- const { sanitizeEntity } = require('@strapi/utils');
10
+ const utils = require('@strapi/utils');
11
11
  const { getService } = require('../utils');
12
- const adminUserController = require('./user/admin');
13
- const apiUserController = require('./user/api');
12
+ const { validateCreateUserBody, validateUpdateUserBody } = require('./validation/user');
14
13
 
15
- const sanitizeUser = user =>
16
- sanitizeEntity(user, {
17
- model: strapi.getModel('plugin::users-permissions.user'),
18
- });
14
+ const { sanitize } = utils;
15
+ const { ApplicationError, ValidationError } = utils.errors;
19
16
 
20
- const resolveController = ctx => {
21
- const {
22
- state: { isAuthenticatedAdmin },
23
- } = ctx;
17
+ const sanitizeOutput = (user, ctx) => {
18
+ const schema = strapi.getModel('plugin::users-permissions.user');
19
+ const { auth } = ctx.state;
24
20
 
25
- return isAuthenticatedAdmin ? adminUserController : apiUserController;
21
+ return sanitize.contentAPI.output(user, schema, { auth });
26
22
  };
27
23
 
28
- const resolveControllerMethod = method => ctx => {
29
- const controller = resolveController(ctx);
30
- const callbackFn = controller[method];
24
+ module.exports = {
25
+ /**
26
+ * Create a/an user record.
27
+ * @return {Object}
28
+ */
29
+ async create(ctx) {
30
+ const advanced = await strapi
31
+ .store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
32
+ .get();
31
33
 
32
- if (!_.isFunction(callbackFn)) {
33
- return ctx.notFound();
34
- }
34
+ await validateCreateUserBody(ctx.request.body);
35
35
 
36
- return callbackFn(ctx);
37
- };
36
+ const { email, username, role } = ctx.request.body;
38
37
 
39
- module.exports = {
40
- create: resolveControllerMethod('create'),
41
- update: resolveControllerMethod('update'),
38
+ const userWithSameUsername = await strapi
39
+ .query('plugin::users-permissions.user')
40
+ .findOne({ where: { username } });
41
+
42
+ if (userWithSameUsername) {
43
+ if (!email) throw new ApplicationError('Username already taken');
44
+ }
45
+
46
+ if (advanced.unique_email) {
47
+ const userWithSameEmail = await strapi
48
+ .query('plugin::users-permissions.user')
49
+ .findOne({ where: { email: email.toLowerCase() } });
50
+
51
+ if (userWithSameEmail) {
52
+ throw new ApplicationError('Email already taken');
53
+ }
54
+ }
55
+
56
+ const user = {
57
+ ...ctx.request.body,
58
+ provider: 'local',
59
+ };
60
+
61
+ user.email = _.toLower(user.email);
62
+
63
+ if (!role) {
64
+ const defaultRole = await strapi
65
+ .query('plugin::users-permissions.role')
66
+ .findOne({ where: { type: advanced.default_role } });
67
+
68
+ user.role = defaultRole.id;
69
+ }
70
+
71
+ try {
72
+ const data = await getService('user').add(user);
73
+ const sanitizedData = await sanitizeOutput(data, ctx);
74
+
75
+ ctx.created(sanitizedData);
76
+ } catch (error) {
77
+ throw new ApplicationError(error.message);
78
+ }
79
+ },
80
+
81
+ /**
82
+ * Update a/an user record.
83
+ * @return {Object}
84
+ */
85
+ async update(ctx) {
86
+ const advancedConfigs = await strapi
87
+ .store({ type: 'plugin', name: 'users-permissions', key: 'advanced' })
88
+ .get();
89
+
90
+ const { id } = ctx.params;
91
+ const { email, username, password } = ctx.request.body;
92
+
93
+ const user = await getService('user').fetch({ id });
94
+
95
+ await validateUpdateUserBody(ctx.request.body);
96
+
97
+ if (user.provider === 'local' && _.has(ctx.request.body, 'password') && !password) {
98
+ throw new ValidationError('password.notNull');
99
+ }
100
+
101
+ if (_.has(ctx.request.body, 'username')) {
102
+ const userWithSameUsername = await strapi
103
+ .query('plugin::users-permissions.user')
104
+ .findOne({ where: { username } });
105
+
106
+ if (userWithSameUsername && userWithSameUsername.id != id) {
107
+ throw new ApplicationError('Username already taken');
108
+ }
109
+ }
110
+
111
+ if (_.has(ctx.request.body, 'email') && advancedConfigs.unique_email) {
112
+ const userWithSameEmail = await strapi
113
+ .query('plugin::users-permissions.user')
114
+ .findOne({ where: { email: email.toLowerCase() } });
115
+
116
+ if (userWithSameEmail && userWithSameEmail.id != id) {
117
+ throw new ApplicationError('Email already taken');
118
+ }
119
+ ctx.request.body.email = ctx.request.body.email.toLowerCase();
120
+ }
121
+
122
+ let updateData = {
123
+ ...ctx.request.body,
124
+ };
125
+
126
+ const data = await getService('user').edit({ id }, updateData);
127
+ const sanitizedData = await sanitizeOutput(data, ctx);
128
+
129
+ ctx.send(sanitizedData);
130
+ },
42
131
 
43
132
  /**
44
133
  * Retrieve user records.
@@ -47,7 +136,7 @@ module.exports = {
47
136
  async find(ctx, next, { populate } = {}) {
48
137
  const users = await getService('user').fetchAll(ctx.query, populate);
49
138
 
50
- ctx.body = users.map(sanitizeUser);
139
+ ctx.body = await Promise.all(users.map(user => sanitizeOutput(user, ctx)));
51
140
  },
52
141
 
53
142
  /**
@@ -59,10 +148,9 @@ module.exports = {
59
148
  let data = await getService('user').fetch({ id });
60
149
 
61
150
  if (data) {
62
- data = sanitizeUser(data);
151
+ data = await sanitizeOutput(data, ctx);
63
152
  }
64
153
 
65
- // Send 200 `ok`
66
154
  ctx.body = data;
67
155
  },
68
156
 
@@ -82,23 +170,9 @@ module.exports = {
82
170
  const { id } = ctx.params;
83
171
 
84
172
  const data = await getService('user').remove({ id });
173
+ const sanitizedUser = await sanitizeOutput(data, ctx);
85
174
 
86
- ctx.send(sanitizeUser(data));
87
- },
88
-
89
- async destroyAll(ctx) {
90
- const {
91
- request: { query },
92
- } = ctx;
93
-
94
- const toRemove = Object.values(_.omit(query, 'source'));
95
-
96
- // FIXME: delete many
97
- const finalQuery = { id: toRemove };
98
-
99
- const data = await getService('user').removeAll(finalQuery);
100
-
101
- ctx.send(data);
175
+ ctx.send(sanitizedUser);
102
176
  },
103
177
 
104
178
  /**
@@ -109,9 +183,9 @@ module.exports = {
109
183
  const user = ctx.state.user;
110
184
 
111
185
  if (!user) {
112
- return ctx.badRequest(null, [{ messages: [{ id: 'No authorization header was found' }] }]);
186
+ return ctx.unauthorized();
113
187
  }
114
188
 
115
- ctx.body = sanitizeUser(user);
189
+ ctx.body = await sanitizeOutput(user, ctx);
116
190
  },
117
191
  };
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ const { yup, validateYupSchema } = require('@strapi/utils');
4
+
5
+ const callbackBodySchema = yup.object().shape({
6
+ identifier: yup.string().required(),
7
+ password: yup.string().required(),
8
+ });
9
+
10
+ const registerBodySchema = yup.object().shape({
11
+ email: yup
12
+ .string()
13
+ .email()
14
+ .required(),
15
+ password: yup.string().required(),
16
+ });
17
+
18
+ const sendEmailConfirmationBodySchema = yup.object().shape({
19
+ email: yup
20
+ .string()
21
+ .email()
22
+ .required(),
23
+ });
24
+
25
+ module.exports = {
26
+ validateCallbackBody: validateYupSchema(callbackBodySchema),
27
+ validateRegisterBody: validateYupSchema(registerBodySchema),
28
+ validateSendEmailConfirmationBody: validateYupSchema(sendEmailConfirmationBodySchema),
29
+ };
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+
3
+ const { yup, validateYupSchema } = require('@strapi/utils');
4
+
5
+ const deleteRoleSchema = yup.object().shape({
6
+ role: yup.strapiID().required(),
7
+ });
8
+
9
+ const createUserBodySchema = yup.object().shape({
10
+ email: yup
11
+ .string()
12
+ .email()
13
+ .required(),
14
+ username: yup
15
+ .string()
16
+ .min(1)
17
+ .required(),
18
+ password: yup
19
+ .string()
20
+ .min(1)
21
+ .required(),
22
+ role: yup.strapiID(),
23
+ });
24
+
25
+ const updateUserBodySchema = yup.object().shape({
26
+ email: yup
27
+ .string()
28
+ .email()
29
+ .min(1),
30
+ username: yup.string().min(1),
31
+ password: yup.string().min(1),
32
+ });
33
+
34
+ module.exports = {
35
+ validateCreateUserBody: validateYupSchema(createUserBodySchema),
36
+ validateUpdateUserBody: validateYupSchema(updateUserBodySchema),
37
+ validateDeleteRoleBody: validateYupSchema(deleteRoleSchema),
38
+ };